Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : //
17 : // u-blox GPS driver for ArduPilot
18 : // Origin code by Michael Smith, Jordi Munoz and Jose Julio, DIYDrones.com
19 : // Substantially rewritten for new GPS driver structure by Andrew Tridgell
20 : //
21 : #include "AP_GPS_UBLOX.h"
22 :
23 : #if AP_GPS_UBLOX_ENABLED
24 :
25 : #if AP_GPS_UBLOX_CFGV2_ENABLED
26 : #include "AP_GPS_UBLOX_CFGv2.h"
27 : #endif
28 :
29 : #include "AP_GPS.h"
30 : #include <AP_HAL/Util.h>
31 : #include <AP_Logger/AP_Logger.h>
32 : #include <GCS_MAVLink/GCS.h>
33 : #include "RTCM3_Parser.h"
34 : #include <stdio.h>
35 :
36 : #ifndef UBLOX_SPEED_CHANGE
37 : #define UBLOX_SPEED_CHANGE 0
38 : #endif
39 :
40 :
41 : #define UBLOX_DEBUGGING 0
42 : #define UBLOX_FAKE_3DLOCK 0
43 : #ifndef CONFIGURE_PPS_PIN
44 : #define CONFIGURE_PPS_PIN 0
45 : #endif
46 :
47 : // this is number of epochs per output. A higher value will reduce
48 : // the uart bandwidth needed and allow for higher latency
49 : #define RTK_MB_RTCM_RATE 1
50 :
51 : // use this to enable debugging of moving baseline configs
52 : #define UBLOX_MB_DEBUGGING 0
53 :
54 : // debug VALGET/VALSET configuration
55 : #define UBLOX_CFG_DEBUGGING 0
56 :
57 : // debug CFGv2 configuration
58 : #define UBLOX_CFGV2_DEBUGGING 0
59 :
60 : extern const AP_HAL::HAL& hal;
61 :
62 : #if UBLOX_DEBUGGING
63 : #if defined(HAL_BUILD_AP_PERIPH)
64 : extern "C" {
65 : void can_printf(const char *fmt, ...);
66 : }
67 : # define Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)
68 : #else
69 : # define Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
70 : #endif
71 : #else
72 : # define Debug(fmt, args ...)
73 : #endif
74 :
75 : #if UBLOX_MB_DEBUGGING
76 : #if defined(HAL_BUILD_AP_PERIPH)
77 : extern "C" {
78 : void can_printf(const char *fmt, ...);
79 : }
80 : # define MB_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)
81 : #else
82 : # define MB_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
83 : #endif
84 : #else
85 : # define MB_Debug(fmt, args ...)
86 : #endif
87 :
88 : #if UBLOX_CFG_DEBUGGING
89 : #if defined(HAL_BUILD_AP_PERIPH)
90 : extern "C" {
91 : void can_printf(const char *fmt, ...);
92 : }
93 : # define CFG_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)
94 : #else
95 : # define CFG_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
96 : #endif
97 : #else
98 : # define CFG_Debug(fmt, args ...)
99 : #endif
100 :
101 : #if UBLOX_CFGV2_DEBUGGING
102 : #if defined(HAL_BUILD_AP_PERIPH)
103 : extern "C" {
104 : void can_printf(const char *fmt, ...);
105 : }
106 : # define CFGv2_Debug(fmt, args ...) do {can_printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args);} while(0)
107 : #else
108 : # define CFGv2_Debug(fmt, args ...) do {hal.console->printf("%s:%d: " fmt "\n", __FUNCTION__, __LINE__, ## args); hal.scheduler->delay(1); } while(0)
109 : #endif
110 : #else
111 : # define CFGv2_Debug(fmt, args ...)
112 : #endif
113 :
114 1023 : AP_GPS_UBLOX::AP_GPS_UBLOX(AP_GPS &_gps,
115 : AP_GPS::Params &_params,
116 : AP_GPS::GPS_State &_state,
117 : AP_HAL::UARTDriver *_port,
118 1023 : AP_GPS::GPS_Role _role) :
119 : AP_GPS_Backend(_gps, _params, _state, _port),
120 1023 : role(_role)
121 : #if AP_GPS_UBLOX_CFGV2_ENABLED
122 1023 : ,_cfg_v2(*this)
123 : #endif
124 : {
125 : // stop any config strings that are pending
126 1023 : gps.send_blob_start(state.instance, nullptr, 0);
127 :
128 : // start the process of updating the GPS rates
129 1023 : _request_next_config();
130 :
131 : #if CONFIGURE_PPS_PIN
132 : _unconfigured_messages |= CONFIG_TP5;
133 : #endif
134 :
135 : #if GPS_MOVING_BASELINE
136 1023 : if (role == AP_GPS::GPS_ROLE_MB_BASE && !mb_use_uart2()) {
137 8 : rtcm3_parser = NEW_NOTHROW RTCM3_Parser;
138 8 : if (rtcm3_parser == nullptr) {
139 0 : GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "u-blox %d: failed RTCMv3 parser allocation", state.instance + 1);
140 : }
141 8 : _unconfigured_messages |= CONFIG_RTK_MOVBASE;
142 : }
143 1023 : if (role == AP_GPS::GPS_ROLE_MB_ROVER) {
144 8 : _unconfigured_messages |= CONFIG_RTK_MOVBASE;
145 8 : state.gps_yaw_configured = true;
146 : }
147 : #endif
148 1023 : }
149 :
150 6 : AP_GPS_UBLOX::~AP_GPS_UBLOX()
151 : {
152 : #if GPS_MOVING_BASELINE
153 3 : delete rtcm3_parser;
154 : #endif
155 :
156 3 : free(config_GNSS);
157 6 : }
158 :
159 : void
160 692829 : AP_GPS_UBLOX::_request_next_config(void)
161 : {
162 : // don't request config if we shouldn't configure the GPS
163 692829 : if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {
164 4688 : return;
165 : }
166 :
167 : // Ensure there is enough space for the largest possible outgoing message
168 688141 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_nav_rate)+2)) {
169 : // not enough space - do it next time
170 0 : return;
171 : }
172 :
173 688141 : if ((_unconfigured_messages & CONFIG_RATE_SOL) != 0 && havePvtMsg) {
174 : /*
175 : we don't need SOL if we have PVT and TIMEGPS. This is needed
176 : as F9P doesn't support the SOL message
177 : */
178 1008 : _unconfigured_messages &= ~CONFIG_RATE_SOL;
179 : }
180 :
181 : Debug("Unconfigured messages: 0x%x Current message: %u\n", (unsigned)_unconfigured_messages, (unsigned)_next_message);
182 :
183 : // check AP_GPS_UBLOX.h for the enum that controls the order.
184 : // This switch statement isn't maintained against the enum in order to reduce code churn
185 688141 : switch (_next_message++) {
186 688141 : case STEP_PVT:
187 688141 : if(!_request_message_rate(CLASS_NAV, MSG_PVT)) {
188 688141 : _next_message--;
189 : }
190 688141 : break;
191 0 : case STEP_TIMEGPS:
192 0 : if(!_request_message_rate(CLASS_NAV, MSG_TIMEGPS)) {
193 0 : _next_message--;
194 : }
195 0 : break;
196 0 : case STEP_PORT:
197 0 : _request_port();
198 0 : break;
199 0 : case STEP_POLL_SVINFO:
200 : // not required once we know what generation we are on
201 0 : if(_hardware_generation == UBLOX_UNKNOWN_HARDWARE_GENERATION) {
202 0 : if (!_send_message(CLASS_NAV, MSG_NAV_SVINFO, 0, 0)) {
203 0 : _next_message--;
204 : }
205 : }
206 0 : break;
207 0 : case STEP_POLL_SBAS:
208 0 : if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {
209 0 : _send_message(CLASS_CFG, MSG_CFG_SBAS, nullptr, 0);
210 : } else {
211 0 : _unconfigured_messages &= ~CONFIG_SBAS;
212 : }
213 0 : break;
214 0 : case STEP_POLL_NAV:
215 0 : if (!_send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS, nullptr, 0)) {
216 0 : _next_message--;
217 : }
218 0 : break;
219 0 : case STEP_POLL_GNSS:
220 0 : if (supports_F9_config()) {
221 0 : if (last_configured_gnss != params.gnss_mode) {
222 0 : _unconfigured_messages |= CONFIG_F9;
223 : }
224 0 : break;
225 : }
226 0 : if (!_send_message(CLASS_CFG, MSG_CFG_GNSS, nullptr, 0)) {
227 0 : _next_message--;
228 : }
229 0 : break;
230 0 : case STEP_POLL_TP5:
231 : #if CONFIGURE_PPS_PIN
232 : if (!_send_message(CLASS_CFG, MSG_CFG_TP5, nullptr, 0)) {
233 : _next_message--;
234 : }
235 : #endif
236 0 : break;
237 0 : case STEP_NAV_RATE:
238 0 : if (!_send_message(CLASS_CFG, MSG_CFG_RATE, nullptr, 0)) {
239 0 : _next_message--;
240 : }
241 0 : break;
242 0 : case STEP_POSLLH:
243 0 : if(!_request_message_rate(CLASS_NAV, MSG_POSLLH)) {
244 0 : _next_message--;
245 : }
246 0 : break;
247 0 : case STEP_STATUS:
248 0 : if(!_request_message_rate(CLASS_NAV, MSG_STATUS)) {
249 0 : _next_message--;
250 : }
251 0 : break;
252 0 : case STEP_SOL:
253 0 : if(!_request_message_rate(CLASS_NAV, MSG_SOL)) {
254 0 : _next_message--;
255 : }
256 0 : break;
257 0 : case STEP_VELNED:
258 0 : if(!_request_message_rate(CLASS_NAV, MSG_VELNED)) {
259 0 : _next_message--;
260 : }
261 0 : break;
262 0 : case STEP_DOP:
263 0 : if(! _request_message_rate(CLASS_NAV, MSG_DOP)) {
264 0 : _next_message--;
265 : }
266 0 : break;
267 0 : case STEP_MON_HW:
268 0 : if(!_request_message_rate(CLASS_MON, MSG_MON_HW)) {
269 0 : _next_message--;
270 : }
271 0 : break;
272 0 : case STEP_MON_HW2:
273 0 : if(!_request_message_rate(CLASS_MON, MSG_MON_HW2)) {
274 0 : _next_message--;
275 : }
276 0 : break;
277 0 : case STEP_RAW:
278 : #if UBLOX_RXM_RAW_LOGGING
279 0 : if(gps._raw_data == 0) {
280 0 : _unconfigured_messages &= ~CONFIG_RATE_RAW;
281 0 : } else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAW)) {
282 0 : _next_message--;
283 : }
284 : #else
285 : _unconfigured_messages &= ~CONFIG_RATE_RAW;
286 : #endif
287 0 : break;
288 0 : case STEP_RAWX:
289 : #if UBLOX_RXM_RAW_LOGGING
290 0 : if(gps._raw_data == 0) {
291 0 : _unconfigured_messages &= ~CONFIG_RATE_RAW;
292 0 : } else if(!_request_message_rate(CLASS_RXM, MSG_RXM_RAWX)) {
293 0 : _next_message--;
294 : }
295 : #else
296 : _unconfigured_messages &= ~CONFIG_RATE_RAW;
297 : #endif
298 0 : break;
299 0 : case STEP_VERSION:
300 0 : if(!_have_version && !hal.util->get_soft_armed()) {
301 0 : _request_version();
302 : } else {
303 0 : _unconfigured_messages &= ~CONFIG_VERSION;
304 : }
305 0 : break;
306 0 : case STEP_TMODE:
307 0 : if (supports_F9_config()) {
308 0 : if (!_configure_valget(ConfigKey::CFG_TMODE_MODE)) {
309 0 : _next_message--;
310 : }
311 : }
312 0 : break;
313 0 : case STEP_RTK_MOVBASE:
314 : #if GPS_MOVING_BASELINE
315 0 : if (supports_F9_config()) {
316 : static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart1), "done_mask too small, base1");
317 : static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Base_uart2), "done_mask too small, base2");
318 : static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart1), "done_mask too small, rover1");
319 : static_assert(sizeof(active_config.done_mask)*8 >= ARRAY_SIZE(config_MB_Rover_uart2), "done_mask too small, rover2");
320 0 : if (role == AP_GPS::GPS_ROLE_MB_BASE) {
321 0 : const config_list *list = mb_use_uart2()?config_MB_Base_uart2:config_MB_Base_uart1;
322 0 : uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Base_uart2):ARRAY_SIZE(config_MB_Base_uart1);
323 0 : if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {
324 0 : _next_message--;
325 : }
326 : }
327 0 : if (role == AP_GPS::GPS_ROLE_MB_ROVER) {
328 0 : const config_list *list = mb_use_uart2()?config_MB_Rover_uart2:config_MB_Rover_uart1;
329 0 : uint8_t list_length = mb_use_uart2()?ARRAY_SIZE(config_MB_Rover_uart2):ARRAY_SIZE(config_MB_Rover_uart1);
330 0 : if (!_configure_config_set(list, list_length, CONFIG_RTK_MOVBASE)) {
331 0 : _next_message--;
332 : }
333 : }
334 : }
335 : #endif
336 0 : break;
337 0 : case STEP_TIM_TM2:
338 : #if UBLOX_TIM_TM2_LOGGING
339 0 : if(!_request_message_rate(CLASS_TIM, MSG_TIM_TM2)) {
340 0 : _next_message--;
341 : }
342 : #else
343 : _unconfigured_messages &= ~CONFIG_TIM_TM2;
344 : #endif
345 0 : break;
346 :
347 0 : case STEP_F9: {
348 0 : if (_hardware_generation == UBLOX_F9 ||
349 0 : _hardware_generation == UBLOX_M10) {
350 0 : uint8_t cfg_count = populate_F9_gnss();
351 : // special handling of F9 config
352 0 : if (cfg_count > 0) {
353 : CFG_Debug("Sending F9 settings, GNSS=%u", unsigned(params.gnss_mode));
354 :
355 0 : if (!_configure_list_valset(config_GNSS, cfg_count, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {
356 0 : _next_message--;
357 0 : break;
358 : }
359 0 : _f9_config_time = AP_HAL::millis();
360 : }
361 : }
362 0 : break;
363 : }
364 :
365 0 : case STEP_F9_VALIDATE: {
366 0 : if (_hardware_generation == UBLOX_F9 ||
367 0 : _hardware_generation == UBLOX_M10) {
368 : // GNSS takes 0.5s to reset
369 0 : if (AP_HAL::millis() - _f9_config_time < 500) {
370 0 : _next_message--;
371 0 : break;
372 : }
373 0 : _f9_config_time = 0;
374 0 : uint8_t cfg_count = populate_F9_gnss();
375 : // special handling of F9 config
376 0 : if (cfg_count > 0) {
377 : CFG_Debug("Validating F9 settings, GNSS=%u", unsigned(params.gnss_mode));
378 : // now validate all of the settings, this is a no-op if the first call succeeded
379 0 : if (!_configure_config_set(config_GNSS, cfg_count, CONFIG_F9, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {
380 0 : _next_message--;
381 : }
382 : }
383 : }
384 0 : break;
385 : }
386 0 : case STEP_M10: {
387 0 : if (_hardware_generation == UBLOX_M10) {
388 : // special handling of M10 config
389 0 : const config_list *list = config_M10;
390 0 : const uint8_t list_length = ARRAY_SIZE(config_M10);
391 : Debug("Sending M10 settings");
392 0 : if (!_configure_config_set(list, list_length, CONFIG_M10, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {
393 0 : _next_message--;
394 : }
395 : }
396 0 : break;
397 : }
398 :
399 0 : case STEP_L5: {
400 0 : if (supports_l5 && option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {
401 0 : const config_list *list = config_L5_ovrd_ena;
402 0 : const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_ena);
403 0 : if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {
404 0 : _next_message--;
405 : }
406 0 : } else if (supports_l5 && !option_set(AP_GPS::DriverOptions::GPSL5HealthOverride)) {
407 0 : const config_list *list = config_L5_ovrd_dis;
408 0 : const uint8_t list_length = ARRAY_SIZE(config_L5_ovrd_dis);
409 0 : if (!_configure_config_set(list, list_length, CONFIG_L5, UBX_VALSET_LAYER_RAM | UBX_VALSET_LAYER_BBR)) {
410 0 : _next_message--;
411 : }
412 : }
413 0 : break;
414 : }
415 :
416 0 : default:
417 : // this case should never be reached, do a full reset if it is hit
418 0 : _next_message = STEP_PVT;
419 0 : break;
420 : }
421 : }
422 :
423 : void
424 0 : AP_GPS_UBLOX::_verify_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate) {
425 : uint8_t desired_rate;
426 : uint32_t config_msg_id;
427 0 : switch(msg_class) {
428 0 : case CLASS_NAV:
429 0 : switch(msg_id) {
430 0 : case MSG_POSLLH:
431 0 : desired_rate = havePvtMsg ? 0 : RATE_POSLLH;
432 0 : config_msg_id = CONFIG_RATE_POSLLH;
433 0 : break;
434 0 : case MSG_STATUS:
435 0 : desired_rate = havePvtMsg ? 0 : RATE_STATUS;
436 0 : config_msg_id = CONFIG_RATE_STATUS;
437 0 : break;
438 0 : case MSG_SOL:
439 0 : desired_rate = havePvtMsg ? 0 : RATE_SOL;
440 0 : config_msg_id = CONFIG_RATE_SOL;
441 0 : break;
442 0 : case MSG_PVT:
443 0 : desired_rate = RATE_PVT;
444 0 : config_msg_id = CONFIG_RATE_PVT;
445 0 : break;
446 0 : case MSG_TIMEGPS:
447 0 : desired_rate = RATE_TIMEGPS;
448 0 : config_msg_id = CONFIG_RATE_TIMEGPS;
449 0 : break;
450 0 : case MSG_VELNED:
451 0 : desired_rate = havePvtMsg ? 0 : RATE_VELNED;
452 0 : config_msg_id = CONFIG_RATE_VELNED;
453 0 : break;
454 0 : case MSG_DOP:
455 0 : desired_rate = RATE_DOP;
456 0 : config_msg_id = CONFIG_RATE_DOP;
457 0 : break;
458 0 : default:
459 0 : return;
460 : }
461 0 : break;
462 0 : case CLASS_MON:
463 0 : switch(msg_id) {
464 0 : case MSG_MON_HW:
465 0 : desired_rate = RATE_HW;
466 0 : config_msg_id = CONFIG_RATE_MON_HW;
467 0 : break;
468 0 : case MSG_MON_HW2:
469 0 : desired_rate = RATE_HW2;
470 0 : config_msg_id = CONFIG_RATE_MON_HW2;
471 0 : break;
472 0 : default:
473 0 : return;
474 : }
475 0 : break;
476 : #if UBLOX_RXM_RAW_LOGGING
477 0 : case CLASS_RXM:
478 0 : switch(msg_id) {
479 0 : case MSG_RXM_RAW:
480 0 : desired_rate = gps._raw_data;
481 0 : config_msg_id = CONFIG_RATE_RAW;
482 0 : break;
483 0 : case MSG_RXM_RAWX:
484 0 : desired_rate = gps._raw_data;
485 0 : config_msg_id = CONFIG_RATE_RAW;
486 0 : break;
487 0 : default:
488 0 : return;
489 : }
490 0 : break;
491 : #endif // UBLOX_RXM_RAW_LOGGING
492 : #if UBLOX_TIM_TM2_LOGGING
493 0 : case CLASS_TIM:
494 0 : if (msg_id == MSG_TIM_TM2) {
495 0 : desired_rate = RATE_TIM_TM2;
496 0 : config_msg_id = CONFIG_TIM_TM2;
497 0 : break;
498 : }
499 0 : return;
500 : #endif // UBLOX_TIM_TM2_LOGGING
501 0 : default:
502 0 : return;
503 : }
504 :
505 0 : if (rate == desired_rate) {
506 : // coming in at correct rate; mark as configured
507 0 : _unconfigured_messages &= ~config_msg_id;
508 0 : return;
509 : }
510 :
511 : // coming in at wrong rate; try to configure it
512 0 : _configure_message_rate(msg_class, msg_id, desired_rate);
513 0 : _unconfigured_messages |= config_msg_id;
514 0 : _cfg_needs_save = true;
515 : }
516 :
517 : // Requests the ublox driver to identify what port we are using to communicate
518 : void
519 688141 : AP_GPS_UBLOX::_request_port(void)
520 : {
521 688141 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+2)) {
522 : // not enough space - do it next time
523 0 : return;
524 : }
525 688141 : _send_message(CLASS_CFG, MSG_CFG_PRT, nullptr, 0);
526 : }
527 :
528 : // Ensure there is enough space for the largest possible outgoing message
529 : // Process bytes available from the stream
530 : //
531 : // The stream is assumed to contain only messages we recognise. If it
532 : // contains other messages, and those messages contain the preamble
533 : // bytes, it is possible for this code to fail to synchronise to the
534 : // stream immediately. Without buffering the entire message and
535 : // re-processing it from the top, this is unavoidable. The parser
536 : // attempts to avoid this when possible.
537 : //
538 : bool
539 6888164 : AP_GPS_UBLOX::read(void)
540 : {
541 6888164 : bool parsed = false;
542 6888164 : uint32_t millis_now = AP_HAL::millis();
543 : #if AP_GPS_UBLOX_CFGV2_ENABLED
544 6888164 : _cfg_v2.update();
545 : #endif
546 : // walk through the gps configuration at 1 message per second
547 13776328 : if (millis_now - _last_config_time >= _delay_time
548 : #if AP_GPS_UBLOX_CFGV2_ENABLED
549 6888164 : && _cfg_v2.using_legacy_config()
550 : #endif
551 : ) {
552 691806 : _request_next_config();
553 691806 : _last_config_time = millis_now;
554 691806 : if (_unconfigured_messages) {
555 : // send the updates faster until fully configured
556 691806 : _delay_time = 200;
557 : } else {
558 0 : _delay_time = 2000;
559 : }
560 : }
561 :
562 : #if 0
563 : // this can be modified to force a particular config state for
564 : // state machine config debugging
565 : static bool done_force_config_error;
566 : if (!_unconfigured_messages && !done_force_config_error) {
567 : done_force_config_error = true;
568 : _unconfigured_messages = CONFIG_F9 | CONFIG_RATE_SOL;
569 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "Forcing config state 0x%04x", unsigned(_unconfigured_messages));
570 : }
571 : #endif
572 :
573 0 : if(!_unconfigured_messages && gps._save_config && !_cfg_saved &&
574 6888164 : _num_cfg_save_tries < 5 && (millis_now - _last_cfg_sent_time) > 5000 &&
575 0 : !hal.util->get_soft_armed()) {
576 : //save the configuration sent until now
577 0 : if (gps._save_config == 1 ||
578 0 : (gps._save_config == 2 && _cfg_needs_save)) {
579 0 : _save_cfg();
580 : }
581 : }
582 :
583 6888164 : const uint16_t numc = MIN(port->available(), 8192U);
584 303399151 : for (uint16_t i = 0; i < numc; i++) { // Process bytes received
585 :
586 : // read the next byte
587 : uint8_t data;
588 296510987 : if (!port->read(data)) {
589 0 : break;
590 : }
591 : #if AP_GPS_DEBUG_LOGGING_ENABLED
592 : log_data(&data, 1);
593 : #endif
594 :
595 : #if GPS_MOVING_BASELINE
596 296510987 : if (rtcm3_parser) {
597 1180068 : if (rtcm3_parser->read(data)) {
598 : // we've found a RTCMv3 packet. We stop parsing at
599 : // this point and reset u-blox parse state. We need to
600 : // stop parsing to give the higher level driver a
601 : // chance to send the RTCMv3 packet to another (rover)
602 : // GPS
603 0 : _step = 0;
604 0 : break;
605 : }
606 : }
607 : #endif
608 :
609 295330919 : reset:
610 : #if AP_GPS_UBLOX_CFGV2_ENABLED
611 296510990 : if (_step == 0) {
612 : // reset the valget state machine
613 4875112 : _cfg_v2.process_valget_complete(false);
614 : }
615 : #endif
616 296510990 : switch(_step) {
617 :
618 : // Message preamble detection
619 : //
620 : // If we fail to match any of the expected bytes, we reset
621 : // the state machine and re-consider the failed byte as
622 : // the first byte of the preamble. This improves our
623 : // chances of recovering from a mismatch and makes it less
624 : // likely that we will be fooled by the preamble appearing
625 : // as data in some other message.
626 : //
627 4870880 : case 1:
628 4870880 : if (PREAMBLE2 == data) {
629 4870880 : _step++;
630 4870880 : break;
631 : }
632 0 : _step = 0;
633 : Debug("reset %u", __LINE__);
634 : FALLTHROUGH;
635 4875112 : case 0:
636 4875112 : if(PREAMBLE1 == data)
637 4870880 : _step++;
638 4875112 : break;
639 :
640 : // Message header processing
641 : //
642 : // We sniff the class and message ID to decide whether we
643 : // are going to gather the message bytes or just discard
644 : // them.
645 : //
646 : // We always collect the length so that we can avoid being
647 : // fooled by preamble bytes in messages.
648 : //
649 4870880 : case 2:
650 4870880 : _step++;
651 4870880 : _class = data;
652 4870880 : _ck_b = _ck_a = data; // reset the checksum accumulators
653 4870880 : break;
654 4870880 : case 3:
655 4870880 : _step++;
656 4870880 : _ck_b += (_ck_a += data); // checksum byte
657 4870880 : _msg_id = data;
658 4870880 : break;
659 4870880 : case 4:
660 4870880 : _step++;
661 4870880 : _ck_b += (_ck_a += data); // checksum byte
662 4870880 : _payload_length = data; // payload length low byte
663 4870880 : break;
664 4870880 : case 5:
665 4870880 : _step++;
666 4870880 : _ck_b += (_ck_a += data); // checksum byte
667 :
668 4870880 : _payload_length += (uint16_t)(data<<8);
669 4870880 : if ((_payload_length > sizeof(_buffer))
670 : #if AP_GPS_UBLOX_CFGV2_ENABLED
671 0 : && !(_class == CLASS_CFG || _msg_id == MSG_CFG_VALGET)
672 : #endif
673 : ) {
674 : Debug("large payload %u", (unsigned)_payload_length);
675 : // assume any payload bigger then what we know about is noise
676 0 : _payload_length = 0;
677 0 : _step = 0;
678 0 : goto reset;
679 : }
680 4870880 : _payload_counter = 0; // prepare to receive payload
681 4870880 : if (_payload_length == 0) {
682 : // bypass payload and go straight to checksum
683 0 : _step++;
684 : }
685 4870880 : break;
686 :
687 : // Receive message data
688 : //
689 257539721 : case 6:
690 257539721 : _ck_b += (_ck_a += data); // checksum byte
691 : #if AP_GPS_UBLOX_CFGV2_ENABLED
692 257539721 : if (_class == CLASS_CFG && _msg_id == MSG_CFG_VALGET) {
693 : CFGv2_Debug("V2 VALGET byte %u/%u: 0x%02x\n", (unsigned)_payload_counter, (unsigned)_payload_length, data);
694 0 : _cfg_v2.process_valget_byte(data);
695 : }
696 : #endif
697 257539721 : if (_payload_counter < sizeof(_buffer)) {
698 257539721 : _buffer[_payload_counter] = data;
699 : }
700 257539721 : if (++_payload_counter == _payload_length)
701 4870880 : _step++;
702 257539721 : break;
703 :
704 : // Checksum and message processing
705 : //
706 4870880 : case 7:
707 4870880 : _step++;
708 4870880 : if (_ck_a != data) {
709 : Debug("bad cka %x should be %x", data, _ck_a);
710 3 : _step = 0;
711 : #if AP_GPS_UBLOX_CFGV2_ENABLED
712 3 : if (_class == CLASS_CFG && _msg_id == MSG_CFG_VALGET) {
713 0 : _cfg_v2.process_valget_complete(false);
714 : }
715 : #endif
716 3 : goto reset;
717 : }
718 4870877 : break;
719 4870877 : case 8:
720 4870877 : _step = 0;
721 4870877 : if (_ck_b != data) {
722 : Debug("bad ckb %x should be %x", data, _ck_b);
723 : #if AP_GPS_UBLOX_CFGV2_ENABLED
724 0 : if (_class == CLASS_CFG && _msg_id == MSG_CFG_VALGET) {
725 0 : _cfg_v2.process_valget_complete(false);
726 : }
727 : #endif
728 0 : break; // bad checksum
729 : }
730 :
731 : #if GPS_MOVING_BASELINE
732 4870877 : if (rtcm3_parser) {
733 : // this is a uBlox packet, discard any partial RTCMv3 state
734 18962 : rtcm3_parser->reset();
735 : }
736 : #endif
737 : #if AP_GPS_UBLOX_CFGV2_ENABLED
738 4870877 : if (_class == CLASS_CFG && _msg_id == MSG_CFG_VALGET) {
739 0 : _cfg_v2.process_valget_complete(true);
740 : }
741 : #endif
742 4870877 : if (_parse_gps()) {
743 695319 : parsed = true;
744 : }
745 4870877 : break;
746 : }
747 : }
748 6888164 : return parsed;
749 : }
750 :
751 : // Private Methods /////////////////////////////////////////////////////////////
752 0 : void AP_GPS_UBLOX::log_mon_hw(void)
753 : {
754 : #if HAL_LOGGING_ENABLED
755 0 : if (!should_log()) {
756 0 : return;
757 : }
758 0 : struct log_Ubx1 pkt = {
759 : LOG_PACKET_HEADER_INIT(LOG_GPS_UBX1_MSG),
760 0 : time_us : AP_HAL::micros64(),
761 0 : instance : state.instance,
762 0 : noisePerMS : _buffer.mon_hw_60.noisePerMS,
763 0 : jamInd : _buffer.mon_hw_60.jamInd,
764 0 : aPower : _buffer.mon_hw_60.aPower,
765 0 : agcCnt : _buffer.mon_hw_60.agcCnt,
766 0 : config : _unconfigured_messages,
767 0 : };
768 0 : if (_payload_length == 68) {
769 0 : pkt.noisePerMS = _buffer.mon_hw_68.noisePerMS;
770 0 : pkt.jamInd = _buffer.mon_hw_68.jamInd;
771 0 : pkt.aPower = _buffer.mon_hw_68.aPower;
772 0 : pkt.agcCnt = _buffer.mon_hw_68.agcCnt;
773 : }
774 0 : AP::logger().WriteBlock(&pkt, sizeof(pkt));
775 : #endif
776 : }
777 :
778 0 : void AP_GPS_UBLOX::log_mon_hw2(void)
779 : {
780 : #if HAL_LOGGING_ENABLED
781 0 : if (!should_log()) {
782 0 : return;
783 : }
784 :
785 0 : struct log_Ubx2 pkt = {
786 : LOG_PACKET_HEADER_INIT(LOG_GPS_UBX2_MSG),
787 0 : time_us : AP_HAL::micros64(),
788 0 : instance : state.instance,
789 0 : ofsI : _buffer.mon_hw2.ofsI,
790 0 : magI : _buffer.mon_hw2.magI,
791 0 : ofsQ : _buffer.mon_hw2.ofsQ,
792 0 : magQ : _buffer.mon_hw2.magQ,
793 0 : };
794 0 : AP::logger().WriteBlock(&pkt, sizeof(pkt));
795 : #endif
796 : }
797 :
798 : #if HAL_LOGGING_ENABLED && AP_GPS_UBLOX_CFGV2_ENABLED
799 0 : void AP_GPS_UBLOX::log_mon_rf(void)
800 : {
801 0 : if (!should_log()) {
802 0 : return;
803 : }
804 : // Use structured buffer
805 0 : const ubx_mon_rf &rf = _buffer.mon_rf;
806 0 : const uint8_t version = rf.version;
807 0 : const uint8_t nBlocks = rf.nBlocks;
808 0 : if (version != 0 || nBlocks == 0) {
809 0 : return;
810 : }
811 0 : const ubx_mon_rf_block &blk0 = rf.blocks[0];
812 :
813 : // Fill UBX1 with RF noise and AGC-like info
814 0 : const struct log_Ubx1 pkt1 {
815 : LOG_PACKET_HEADER_INIT(LOG_GPS_UBX1_MSG),
816 0 : time_us : AP_HAL::micros64(),
817 0 : instance : state.instance,
818 0 : noisePerMS : blk0.noisePerMS,
819 0 : jamInd : uint8_t(blk0.flags & 0x03U), // jammingState per spec (0..3)
820 0 : aPower : blk0.antPower,
821 0 : agcCnt : blk0.agcCnt,
822 : config : 0, // not used for CFGv2
823 0 : };
824 0 : AP::logger().WriteBlock(&pkt1, sizeof(pkt1));
825 :
826 : // Fill UBX2 with I/Q imbalance and magnitudes
827 0 : const struct log_Ubx2 pkt2 {
828 : LOG_PACKET_HEADER_INIT(LOG_GPS_UBX2_MSG),
829 0 : time_us : AP_HAL::micros64(),
830 0 : instance : state.instance,
831 0 : ofsI : blk0.ofsI,
832 0 : magI : blk0.magI,
833 0 : ofsQ : blk0.ofsQ,
834 0 : magQ : blk0.magQ,
835 0 : };
836 0 : AP::logger().WriteBlock(&pkt2, sizeof(pkt2));
837 : }
838 : #endif // HAL_LOGGING_ENABLED && AP_GPS_UBLOX_CFGV2_ENABLED
839 :
840 : #if UBLOX_TIM_TM2_LOGGING
841 0 : void AP_GPS_UBLOX::log_tim_tm2(void)
842 : {
843 : #if HAL_LOGGING_ENABLED
844 0 : if (!should_log()) {
845 0 : return;
846 : }
847 :
848 : // @LoggerMessage: UBXT
849 : // @Description: uBlox specific UBX-TIM-TM2 logging, see uBlox interface description
850 : // @Field: TimeUS: Time since system startup
851 : // @Field: I: GPS instance number
852 : // @Field: ch: Channel (i.e. EXTINT) upon which the pulse was measured
853 : // @Field: flags: Bitmask
854 : // @Field: count: Rising edge counter
855 : // @Field: wnR: Week number of last rising edge
856 : // @Field: MsR: Tow of rising edge
857 : // @Field: SubMsR: Millisecond fraction of tow of rising edge in nanoseconds
858 : // @Field: wnF: Week number of last falling edge
859 : // @Field: MsF: Tow of falling edge
860 : // @Field: SubMsF: Millisecond fraction of tow of falling edge in nanoseconds
861 : // @Field: accEst: Accuracy estimate
862 :
863 0 : AP::logger().WriteStreaming("UBXT",
864 : "TimeUS,I,ch,flags,count,wnR,MsR,SubMsR,wnF,MsF,SubMsF,accEst",
865 : "s#----ss-sss",
866 : "F-----CI-CII",
867 : "QBBBHHIIHIII",
868 : AP_HAL::micros64(),
869 0 : state.instance,
870 0 : _buffer.tim_tm2.ch,
871 0 : _buffer.tim_tm2.flags,
872 0 : _buffer.tim_tm2.count,
873 0 : _buffer.tim_tm2.wnR,
874 : _buffer.tim_tm2.towMsR,
875 : _buffer.tim_tm2.towSubMsR,
876 0 : _buffer.tim_tm2.wnF,
877 : _buffer.tim_tm2.towMsF,
878 : _buffer.tim_tm2.towSubMsF,
879 : _buffer.tim_tm2.accEst);
880 : #endif
881 : }
882 : #endif // UBLOX_TIM_TM2_LOGGING
883 :
884 : #if UBLOX_RXM_RAW_LOGGING
885 0 : void AP_GPS_UBLOX::log_rxm_raw(const struct ubx_rxm_raw &raw)
886 : {
887 : #if HAL_LOGGING_ENABLED
888 0 : if (!should_log()) {
889 0 : return;
890 : }
891 :
892 0 : uint64_t now = AP_HAL::micros64();
893 0 : for (uint8_t i=0; i<raw.numSV; i++) {
894 0 : struct log_GPS_RAW pkt = {
895 : LOG_PACKET_HEADER_INIT(LOG_GPS_RAW_MSG),
896 : time_us : now,
897 0 : iTOW : raw.iTOW,
898 0 : week : raw.week,
899 0 : numSV : raw.numSV,
900 0 : sv : raw.svinfo[i].sv,
901 0 : cpMes : raw.svinfo[i].cpMes,
902 0 : prMes : raw.svinfo[i].prMes,
903 0 : doMes : raw.svinfo[i].doMes,
904 0 : mesQI : raw.svinfo[i].mesQI,
905 0 : cno : raw.svinfo[i].cno,
906 0 : lli : raw.svinfo[i].lli
907 0 : };
908 0 : AP::logger().WriteBlock(&pkt, sizeof(pkt));
909 : }
910 : #endif
911 : }
912 :
913 0 : void AP_GPS_UBLOX::log_rxm_rawx(const struct ubx_rxm_rawx &raw)
914 : {
915 : #if HAL_LOGGING_ENABLED
916 0 : if (!should_log()) {
917 0 : return;
918 : }
919 :
920 0 : uint64_t now = AP_HAL::micros64();
921 :
922 0 : struct log_GPS_RAWH header = {
923 : LOG_PACKET_HEADER_INIT(LOG_GPS_RAWH_MSG),
924 : time_us : now,
925 0 : rcvTow : raw.rcvTow,
926 0 : week : raw.week,
927 0 : leapS : raw.leapS,
928 0 : numMeas : raw.numMeas,
929 0 : recStat : raw.recStat
930 0 : };
931 0 : AP::logger().WriteBlock(&header, sizeof(header));
932 :
933 0 : for (uint8_t i=0; i<raw.numMeas; i++) {
934 0 : struct log_GPS_RAWS pkt = {
935 : LOG_PACKET_HEADER_INIT(LOG_GPS_RAWS_MSG),
936 : time_us : now,
937 0 : prMes : raw.svinfo[i].prMes,
938 0 : cpMes : raw.svinfo[i].cpMes,
939 0 : doMes : raw.svinfo[i].doMes,
940 0 : gnssId : raw.svinfo[i].gnssId,
941 0 : svId : raw.svinfo[i].svId,
942 0 : freqId : raw.svinfo[i].freqId,
943 0 : locktime : raw.svinfo[i].locktime,
944 0 : cno : raw.svinfo[i].cno,
945 0 : prStdev : raw.svinfo[i].prStdev,
946 0 : cpStdev : raw.svinfo[i].cpStdev,
947 0 : doStdev : raw.svinfo[i].doStdev,
948 0 : trkStat : raw.svinfo[i].trkStat
949 0 : };
950 0 : AP::logger().WriteBlock(&pkt, sizeof(pkt));
951 : }
952 : #endif
953 : }
954 : #endif // UBLOX_RXM_RAW_LOGGING
955 :
956 0 : void AP_GPS_UBLOX::unexpected_message(void)
957 : {
958 : Debug("Unexpected message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);
959 0 : if (++_disable_counter == 0) {
960 : // disable future sends of this message id, but
961 : // only do this every 256 messages, as some
962 : // message types can't be disabled and we don't
963 : // want to get into an ack war
964 : Debug("Disabling message 0x%02x 0x%02x", (unsigned)_class, (unsigned)_msg_id);
965 0 : _configure_message_rate(_class, _msg_id, 0);
966 : }
967 0 : }
968 :
969 : // return size of a config key, or 0 if unknown
970 0 : uint8_t AP_GPS_UBLOX::config_key_size(ConfigKey key) const
971 : {
972 : // step over the value
973 0 : const uint8_t key_size = (uint32_t(key) >> 28) & 0x07; // mask off the storage size
974 0 : switch (key_size) {
975 0 : case 0x1: // bit
976 : case 0x2: // byte
977 0 : return 1;
978 0 : case 0x3: // 2 bytes
979 0 : return 2;
980 0 : case 0x4: // 4 bytes
981 0 : return 4;
982 0 : case 0x5: // 8 bytes
983 0 : return 8;
984 0 : default:
985 0 : break;
986 : }
987 : // invalid
988 0 : return 0;
989 : }
990 :
991 : /*
992 : find index of a config key in the active_config list, or -1
993 : */
994 0 : int8_t AP_GPS_UBLOX::find_active_config_index(ConfigKey key) const
995 : {
996 0 : if (active_config.list == nullptr) {
997 0 : return -1;
998 : }
999 0 : for (uint8_t i=0; i<active_config.count; i++) {
1000 0 : if (key == active_config.list[i].key) {
1001 0 : return (int8_t)i;
1002 : }
1003 : }
1004 :
1005 0 : return -1;
1006 : }
1007 :
1008 : bool
1009 : #if AP_GPS_UBLOX_CFGV2_ENABLED
1010 4870877 : AP_GPS_UBLOX::_legacy_config_update(void)
1011 : #else
1012 : AP_GPS_UBLOX::_parse_gps(void)
1013 : #endif
1014 : {
1015 4870877 : if (_class == CLASS_ACK) {
1016 : Debug("ACK %u", (unsigned)_msg_id);
1017 :
1018 0 : if(_msg_id == MSG_ACK_ACK) {
1019 0 : switch(_buffer.ack.clsID) {
1020 0 : case CLASS_CFG:
1021 0 : switch(_buffer.ack.msgID) {
1022 0 : case MSG_CFG_CFG:
1023 0 : _cfg_saved = true;
1024 0 : _cfg_needs_save = false;
1025 0 : break;
1026 0 : case MSG_CFG_GNSS:
1027 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1028 0 : break;
1029 0 : case MSG_CFG_MSG:
1030 : // There is no way to know what MSG config was ack'ed, assume it was the last
1031 : // one requested. To verify it rerequest the last config we sent. If we miss
1032 : // the actual ack we will catch it next time through the poll loop, but that
1033 : // will be a good chunk of time later.
1034 0 : break;
1035 0 : case MSG_CFG_NAV_SETTINGS:
1036 0 : _unconfigured_messages &= ~CONFIG_NAV_SETTINGS;
1037 0 : break;
1038 0 : case MSG_CFG_RATE:
1039 : // The GPS will ACK a update rate that is invalid. in order to detect this
1040 : // only accept the rate as configured by reading the settings back and
1041 : // validating that they all match the target values
1042 0 : break;
1043 0 : case MSG_CFG_SBAS:
1044 0 : _unconfigured_messages &= ~CONFIG_SBAS;
1045 0 : break;
1046 0 : case MSG_CFG_TP5:
1047 0 : _unconfigured_messages &= ~CONFIG_TP5;
1048 0 : break;
1049 : }
1050 :
1051 0 : break;
1052 0 : case CLASS_MON:
1053 0 : switch(_buffer.ack.msgID) {
1054 0 : case MSG_MON_HW:
1055 0 : _unconfigured_messages &= ~CONFIG_RATE_MON_HW;
1056 0 : break;
1057 0 : case MSG_MON_HW2:
1058 0 : _unconfigured_messages &= ~CONFIG_RATE_MON_HW2;
1059 0 : break;
1060 : }
1061 : }
1062 : }
1063 0 : if(_msg_id == MSG_ACK_NACK) {
1064 0 : switch(_buffer.nack.clsID) {
1065 0 : case CLASS_CFG:
1066 0 : switch(_buffer.nack.msgID) {
1067 0 : case MSG_CFG_VALGET:
1068 : CFG_Debug("NACK VALGET 0x%x", (unsigned)_buffer.nack.msgID);
1069 0 : if (active_config.list != nullptr) {
1070 : /*
1071 : likely this device does not support fetching multiple keys at once, go one at a time
1072 : */
1073 0 : if (active_config.fetch_index == -1) {
1074 : CFG_Debug("NACK starting %u", unsigned(active_config.count));
1075 0 : active_config.fetch_index = 0;
1076 0 : use_single_valget = true;
1077 : } else {
1078 : // the device does not support the config key we asked for,
1079 : // consider the bit as done
1080 0 : active_config.done_mask |= (1U<<active_config.fetch_index);
1081 : CFG_Debug("NACK %d 0x%x done=0x%x",
1082 : int(active_config.fetch_index),
1083 : unsigned(active_config.list[active_config.fetch_index].key),
1084 : unsigned(active_config.done_mask));
1085 0 : if (active_config.done_mask == (1U<<active_config.count)-1 ||
1086 0 : active_config.fetch_index >= active_config.count) {
1087 : // all done!
1088 0 : _unconfigured_messages &= ~active_config.unconfig_bit;
1089 : }
1090 0 : active_config.fetch_index++;
1091 : }
1092 0 : if (active_config.fetch_index < active_config.count) {
1093 0 : _configure_valget(active_config.list[active_config.fetch_index].key);
1094 : }
1095 : }
1096 0 : break;
1097 0 : case MSG_CFG_VALSET:
1098 0 : if (active_config.list != nullptr) {
1099 : CFG_Debug("NACK VALSET 0x%x 0x%x", (unsigned)_buffer.nack.msgID,
1100 : unsigned(active_config.list[active_config.set_index].key));
1101 0 : if (is_gnss_key(active_config.list[active_config.set_index].key)) {
1102 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "GPS %u: unable to configure band 0x%02x",
1103 : unsigned(state.instance + 1), unsigned(active_config.list[active_config.set_index].key));
1104 :
1105 : }
1106 : }
1107 0 : break;
1108 : }
1109 : }
1110 : }
1111 0 : return false;
1112 : }
1113 :
1114 4870877 : if (_class == CLASS_CFG) {
1115 0 : switch(_msg_id) {
1116 0 : case MSG_CFG_NAV_SETTINGS:
1117 : Debug("Got settings %u min_elev %d drLimit %u\n",
1118 : (unsigned)_buffer.nav_settings.dynModel,
1119 : (int)_buffer.nav_settings.minElev,
1120 : (unsigned)_buffer.nav_settings.drLimit);
1121 0 : _buffer.nav_settings.mask = 0;
1122 0 : if (gps._navfilter != AP_GPS::GPS_ENGINE_NONE &&
1123 0 : _buffer.nav_settings.dynModel != gps._navfilter) {
1124 : // we've received the current nav settings, change the engine
1125 : // settings and send them back
1126 : Debug("Changing engine setting from %u to %u\n",
1127 : (unsigned)_buffer.nav_settings.dynModel, (unsigned)gps._navfilter);
1128 0 : _buffer.nav_settings.dynModel = gps._navfilter;
1129 0 : _buffer.nav_settings.mask |= 1;
1130 : }
1131 0 : if (gps._min_elevation != -100 &&
1132 0 : _buffer.nav_settings.minElev != gps._min_elevation) {
1133 : Debug("Changing min elevation to %d\n", (int)gps._min_elevation);
1134 0 : _buffer.nav_settings.minElev = gps._min_elevation;
1135 0 : _buffer.nav_settings.mask |= 2;
1136 : }
1137 0 : if (_buffer.nav_settings.mask != 0) {
1138 0 : _send_message(CLASS_CFG, MSG_CFG_NAV_SETTINGS,
1139 0 : &_buffer.nav_settings,
1140 : sizeof(_buffer.nav_settings));
1141 0 : _unconfigured_messages |= CONFIG_NAV_SETTINGS;
1142 0 : _cfg_needs_save = true;
1143 : } else {
1144 0 : _unconfigured_messages &= ~CONFIG_NAV_SETTINGS;
1145 : }
1146 0 : return false;
1147 :
1148 : #if UBLOX_GNSS_SETTINGS
1149 0 : case MSG_CFG_GNSS:
1150 0 : if (params.gnss_mode != 0 && !supports_F9_config()) {
1151 0 : struct ubx_cfg_gnss start_gnss = _buffer.gnss;
1152 0 : uint8_t gnssCount = 0;
1153 : Debug("Got GNSS Settings %u %u %u %u:\n",
1154 : (unsigned)_buffer.gnss.msgVer,
1155 : (unsigned)_buffer.gnss.numTrkChHw,
1156 : (unsigned)_buffer.gnss.numTrkChUse,
1157 : (unsigned)_buffer.gnss.numConfigBlocks);
1158 : #if UBLOX_DEBUGGING
1159 : for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {
1160 : Debug(" %u %u %u 0x%08x\n",
1161 : (unsigned)_buffer.gnss.configBlock[i].gnssId,
1162 : (unsigned)_buffer.gnss.configBlock[i].resTrkCh,
1163 : (unsigned)_buffer.gnss.configBlock[i].maxTrkCh,
1164 : (unsigned)_buffer.gnss.configBlock[i].flags);
1165 : }
1166 : #endif
1167 :
1168 0 : for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {
1169 0 : if((params.gnss_mode & (1 << i)) && i != GNSS_SBAS) {
1170 0 : gnssCount++;
1171 : }
1172 : }
1173 0 : for(int i = 0; i < _buffer.gnss.numConfigBlocks; i++) {
1174 : // Reserve an equal portion of channels for all enabled systems that supports it
1175 0 : if(params.gnss_mode & (1 << _buffer.gnss.configBlock[i].gnssId)) {
1176 0 : if(GNSS_SBAS !=_buffer.gnss.configBlock[i].gnssId && (_hardware_generation > UBLOX_M8 || GNSS_GAL !=_buffer.gnss.configBlock[i].gnssId)) {
1177 0 : _buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);
1178 0 : _buffer.gnss.configBlock[i].maxTrkCh = _buffer.gnss.numTrkChHw;
1179 : } else {
1180 0 : if(GNSS_SBAS ==_buffer.gnss.configBlock[i].gnssId) {
1181 0 : _buffer.gnss.configBlock[i].resTrkCh = 1;
1182 0 : _buffer.gnss.configBlock[i].maxTrkCh = 3;
1183 : }
1184 0 : if(GNSS_GAL ==_buffer.gnss.configBlock[i].gnssId) {
1185 0 : _buffer.gnss.configBlock[i].resTrkCh = (_buffer.gnss.numTrkChHw - 3) / (gnssCount * 2);
1186 0 : _buffer.gnss.configBlock[i].maxTrkCh = 8; //Per the M8 receiver description UBX-13003221 - R16, 4.1.1.3 it is not recommended to set the number of galileo channels higher then eight
1187 : }
1188 : }
1189 0 : _buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags | 0x00000001;
1190 : } else {
1191 0 : _buffer.gnss.configBlock[i].resTrkCh = 0;
1192 0 : _buffer.gnss.configBlock[i].maxTrkCh = 0;
1193 0 : _buffer.gnss.configBlock[i].flags = _buffer.gnss.configBlock[i].flags & 0xFFFFFFFE;
1194 : }
1195 : }
1196 0 : if (memcmp(&start_gnss, &_buffer.gnss, sizeof(start_gnss))) {
1197 0 : _send_message(CLASS_CFG, MSG_CFG_GNSS, &_buffer.gnss, 4 + (8 * _buffer.gnss.numConfigBlocks));
1198 0 : _unconfigured_messages |= CONFIG_GNSS;
1199 0 : _cfg_needs_save = true;
1200 : } else {
1201 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1202 : }
1203 : } else {
1204 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1205 : }
1206 0 : return false;
1207 : #endif
1208 :
1209 0 : case MSG_CFG_SBAS:
1210 0 : if (gps._sbas_mode != AP_GPS::SBAS_Mode::DoNotChange) {
1211 : Debug("Got SBAS settings %u %u %u 0x%x 0x%x\n",
1212 : (unsigned)_buffer.sbas.mode,
1213 : (unsigned)_buffer.sbas.usage,
1214 : (unsigned)_buffer.sbas.maxSBAS,
1215 : (unsigned)_buffer.sbas.scanmode2,
1216 : (unsigned)_buffer.sbas.scanmode1);
1217 0 : if (_buffer.sbas.mode != gps._sbas_mode) {
1218 0 : _buffer.sbas.mode = gps._sbas_mode;
1219 0 : _send_message(CLASS_CFG, MSG_CFG_SBAS,
1220 0 : &_buffer.sbas,
1221 : sizeof(_buffer.sbas));
1222 0 : _unconfigured_messages |= CONFIG_SBAS;
1223 0 : _cfg_needs_save = true;
1224 : } else {
1225 0 : _unconfigured_messages &= ~CONFIG_SBAS;
1226 : }
1227 : } else {
1228 0 : _unconfigured_messages &= ~CONFIG_SBAS;
1229 : }
1230 0 : return false;
1231 0 : case MSG_CFG_MSG:
1232 0 : if(_payload_length == sizeof(ubx_cfg_msg_rate_6)) {
1233 : // can't verify the setting without knowing the port
1234 : // request the port again
1235 0 : if(_ublox_port >= UBLOX_MAX_PORTS) {
1236 0 : _request_port();
1237 0 : return false;
1238 : }
1239 0 : _verify_rate(_buffer.msg_rate_6.msg_class, _buffer.msg_rate_6.msg_id,
1240 0 : _buffer.msg_rate_6.rates[_ublox_port]);
1241 : } else {
1242 0 : _verify_rate(_buffer.msg_rate.msg_class, _buffer.msg_rate.msg_id,
1243 0 : _buffer.msg_rate.rate);
1244 : }
1245 0 : return false;
1246 0 : case MSG_CFG_PRT:
1247 0 : _ublox_port = _buffer.prt.portID;
1248 0 : return false;
1249 0 : case MSG_CFG_RATE:
1250 0 : if(_buffer.nav_rate.measure_rate_ms != params.rate_ms ||
1251 0 : _buffer.nav_rate.nav_rate != 1 ||
1252 0 : _buffer.nav_rate.timeref != 0) {
1253 0 : _configure_rate();
1254 0 : _unconfigured_messages |= CONFIG_RATE_NAV;
1255 0 : _cfg_needs_save = true;
1256 : } else {
1257 0 : _unconfigured_messages &= ~CONFIG_RATE_NAV;
1258 : }
1259 0 : return false;
1260 :
1261 : #if CONFIGURE_PPS_PIN
1262 : case MSG_CFG_TP5: {
1263 : // configure the PPS pin for 1Hz, zero delay
1264 : Debug("Got TP5 ver=%u 0x%04x %u\n",
1265 : (unsigned)_buffer.nav_tp5.version,
1266 : (unsigned)_buffer.nav_tp5.flags,
1267 : (unsigned)_buffer.nav_tp5.freqPeriod);
1268 : #ifdef HAL_GPIO_PPS
1269 : hal.gpio->attach_interrupt(HAL_GPIO_PPS, FUNCTOR_BIND_MEMBER(&AP_GPS_UBLOX::pps_interrupt, void, uint8_t, bool, uint32_t), AP_HAL::GPIO::INTERRUPT_FALLING);
1270 : #endif
1271 : const uint16_t desired_flags = 0x003f;
1272 : const uint16_t desired_period_hz = _pps_freq;
1273 :
1274 : if (_buffer.nav_tp5.flags != desired_flags ||
1275 : _buffer.nav_tp5.freqPeriod != desired_period_hz) {
1276 : _buffer.nav_tp5.tpIdx = 0;
1277 : _buffer.nav_tp5.reserved1[0] = 0;
1278 : _buffer.nav_tp5.reserved1[1] = 0;
1279 : _buffer.nav_tp5.antCableDelay = 0;
1280 : _buffer.nav_tp5.rfGroupDelay = 0;
1281 : _buffer.nav_tp5.freqPeriod = desired_period_hz;
1282 : _buffer.nav_tp5.freqPeriodLock = desired_period_hz;
1283 : _buffer.nav_tp5.pulseLenRatio = 1;
1284 : _buffer.nav_tp5.pulseLenRatioLock = 2;
1285 : _buffer.nav_tp5.userConfigDelay = 0;
1286 : _buffer.nav_tp5.flags = desired_flags;
1287 : _send_message(CLASS_CFG, MSG_CFG_TP5,
1288 : &_buffer.nav_tp5,
1289 : sizeof(_buffer.nav_tp5));
1290 : _unconfigured_messages |= CONFIG_TP5;
1291 : _cfg_needs_save = true;
1292 : } else {
1293 : _unconfigured_messages &= ~CONFIG_TP5;
1294 : }
1295 : return false;
1296 : }
1297 : #endif // CONFIGURE_PPS_PIN
1298 0 : case MSG_CFG_VALGET: {
1299 0 : uint8_t cfg_len = _payload_length - sizeof(ubx_cfg_valget);
1300 0 : const uint8_t *cfg_data = (const uint8_t *)(&_buffer) + sizeof(ubx_cfg_valget);
1301 0 : while (cfg_len >= 5) {
1302 : ConfigKey id;
1303 0 : memcpy(&id, cfg_data, sizeof(uint32_t));
1304 0 : cfg_len -= 4;
1305 0 : cfg_data += 4;
1306 0 : switch (id) {
1307 0 : case ConfigKey::CFG_TMODE_MODE: {
1308 0 : uint8_t mode = cfg_data[0];
1309 0 : if (mode != 0) {
1310 : // ask for mode 0, to disable TIME mode
1311 0 : mode = 0;
1312 0 : _configure_valset(ConfigKey::CFG_TMODE_MODE, &mode);
1313 0 : _cfg_needs_save = true;
1314 0 : _unconfigured_messages |= CONFIG_TMODE_MODE;
1315 : } else {
1316 0 : _unconfigured_messages &= ~CONFIG_TMODE_MODE;
1317 : }
1318 0 : break;
1319 : }
1320 0 : default:
1321 0 : break;
1322 : }
1323 : // see if it is in active config list
1324 0 : int8_t cfg_idx = find_active_config_index(id);
1325 0 : if (cfg_idx >= 0) {
1326 : CFG_Debug("valset(0x%lx): %u", (long unsigned)id, unsigned((*cfg_data) & 0x1));
1327 0 : const uint8_t key_size = config_key_size(id);
1328 0 : if (cfg_len < key_size
1329 : // for keys of length 1 only the LSB is significant
1330 0 : || (key_size == 1 && (active_config.list[cfg_idx].value & 0x1) != (*cfg_data & 0x1))
1331 0 : || memcmp(&active_config.list[cfg_idx].value, cfg_data, key_size) != 0) {
1332 0 : _configure_valset(id, &active_config.list[cfg_idx].value, active_config.layers);
1333 0 : _unconfigured_messages |= active_config.unconfig_bit;
1334 0 : active_config.done_mask &= ~(1U << cfg_idx);
1335 0 : active_config.set_index = cfg_idx;
1336 0 : _cfg_needs_save = true;
1337 : } else {
1338 0 : active_config.done_mask |= (1U << cfg_idx);
1339 : CFG_Debug("done %u mask=0x%x all_mask=0x%x",
1340 : unsigned(cfg_idx),
1341 : unsigned(active_config.done_mask),
1342 : (1U<<active_config.count)-1);
1343 0 : if (active_config.done_mask == (1U<<active_config.count)-1) {
1344 : // all done!
1345 0 : _unconfigured_messages &= ~active_config.unconfig_bit;
1346 : }
1347 : }
1348 0 : if (active_config.fetch_index >= 0 &&
1349 0 : active_config.fetch_index < active_config.count &&
1350 0 : id == active_config.list[active_config.fetch_index].key) {
1351 0 : active_config.fetch_index++;
1352 0 : if (active_config.fetch_index < active_config.count) {
1353 0 : _configure_valget(active_config.list[active_config.fetch_index].key);
1354 : CFG_Debug("valget %d 0x%x", int(active_config.fetch_index),
1355 : unsigned(active_config.list[active_config.fetch_index].key));
1356 : }
1357 : }
1358 : } else {
1359 : CFG_Debug("valget no active config for 0x%lx", (long unsigned)id);
1360 : }
1361 :
1362 : // step over the value
1363 0 : uint8_t step_size = config_key_size(id);
1364 0 : if (step_size == 0) {
1365 0 : return false;
1366 : }
1367 0 : cfg_len -= step_size;
1368 0 : cfg_data += step_size;
1369 : }
1370 : }
1371 : }
1372 : }
1373 : #if AP_GPS_UBLOX_CFGV2_ENABLED
1374 4870877 : return true;
1375 : }
1376 :
1377 : bool
1378 4870877 : AP_GPS_UBLOX::_parse_gps(void)
1379 : {
1380 4870877 : if (_legacy_cfg_supported && !gps.option_set(AP_GPS::ForceUBXConfigV2)) {
1381 4870877 : if (!_legacy_config_update()) {
1382 0 : return false;
1383 : }
1384 : }
1385 : // call again to parse any responses from CFGV2
1386 4870877 : _cfg_v2.update();
1387 :
1388 : #endif
1389 :
1390 4870877 : if (_class == CLASS_MON) {
1391 0 : switch(_msg_id) {
1392 0 : case MSG_MON_HW:
1393 0 : if (_payload_length == 60 || _payload_length == 68) {
1394 0 : log_mon_hw();
1395 : }
1396 0 : break;
1397 0 : case MSG_MON_HW2:
1398 0 : if (_payload_length == 28) {
1399 0 : log_mon_hw2();
1400 : }
1401 0 : break;
1402 : #if HAL_LOGGING_ENABLED && AP_GPS_UBLOX_CFGV2_ENABLED
1403 0 : case MSG_MON_RF:
1404 : // forward to logger when present
1405 0 : log_mon_rf();
1406 0 : break;
1407 : #endif // HAL_LOGGING_ENABLED
1408 0 : case MSG_MON_VER: {
1409 0 : bool check_L1L5 = false;
1410 0 : _have_version = true;
1411 0 : strncpy(_version.hwVersion, _buffer.mon_ver.hwVersion, sizeof(_version.hwVersion));
1412 0 : strncpy(_version.swVersion, _buffer.mon_ver.swVersion, sizeof(_version.swVersion));
1413 0 : char* mod = (char*)memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "MOD=", 4);
1414 : #if AP_GPS_UBLOX_CFGV2_ENABLED
1415 0 : char* protver_end = _buffer.mon_ver.extension + sizeof(_buffer.mon_ver.extension);
1416 : #endif
1417 0 : if (mod != nullptr
1418 : #if AP_GPS_UBLOX_CFGV2_ENABLED
1419 0 : && (mod+4+UBLOX_MODULE_LEN-1) < protver_end
1420 : #endif
1421 : ) {
1422 0 : strncpy(_module, mod+4, UBLOX_MODULE_LEN-1);
1423 : }
1424 : #if AP_GPS_UBLOX_CFGV2_ENABLED
1425 0 : char* prot = (char*)memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "PROTVER=", 8);
1426 0 : if (prot != nullptr && (prot+8+UBLOX_PROTVER_LEN-1) < protver_end) {
1427 : Debug("Found PROTVER string %.20s", prot+8);
1428 0 : strncpy(_protver, prot+8, UBLOX_PROTVER_LEN-1);
1429 : }
1430 : #endif
1431 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO,
1432 : "u-blox %s%s%d HW: %s SW: %s",
1433 : _module, mod != nullptr ? " " : "",
1434 : state.instance + 1,
1435 : _version.hwVersion,
1436 : _version.swVersion);
1437 : // check for F9 and M9. The F9 does not respond to SVINFO,
1438 : // so we need to use MON_VER for hardware generation
1439 0 : if (strncmp(_version.hwVersion, "00190000", 8) == 0) {
1440 0 : if (strncmp(_version.swVersion, "EXT CORE 1", 10) == 0) {
1441 : // a F9
1442 0 : if (_hardware_generation != UBLOX_F9) {
1443 : // need to ensure time mode is correctly setup on F9
1444 0 : _unconfigured_messages |= CONFIG_TMODE_MODE;
1445 : }
1446 0 : _hardware_generation = UBLOX_F9;
1447 0 : _unconfigured_messages |= CONFIG_F9;
1448 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1449 0 : if (strncmp(_module, "ZED-F9P", UBLOX_MODULE_LEN) == 0) {
1450 0 : _hardware_variant = UBLOX_F9_ZED;
1451 0 : } else if (strncmp(_module, "NEO-F9P", UBLOX_MODULE_LEN) == 0) {
1452 0 : _hardware_variant = UBLOX_F9_NEO;
1453 : }
1454 : }
1455 0 : if (strncmp(_version.swVersion, "EXT CORE 4", 10) == 0) {
1456 : // a M9
1457 0 : _hardware_generation = UBLOX_M9;
1458 : }
1459 0 : check_L1L5 = true;
1460 : }
1461 : // check for M10
1462 0 : if (strncmp(_version.hwVersion, "000A0000", 8) == 0) {
1463 0 : _hardware_generation = UBLOX_M10;
1464 0 : _unconfigured_messages |= CONFIG_M10;
1465 : // M10 does not support CONFIG_GNSS
1466 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1467 0 : check_L1L5 = true;
1468 :
1469 : // M10 does not support multi-valued VALGET
1470 0 : use_single_valget = true;
1471 : }
1472 0 : if (check_L1L5) {
1473 : // check if L1L5 in extension
1474 0 : if (memmem(_buffer.mon_ver.extension, sizeof(_buffer.mon_ver.extension), "L1L5", 4) != nullptr) {
1475 0 : supports_l5 = true;
1476 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "u-blox supports L5 Band");
1477 0 : _unconfigured_messages |= CONFIG_L5;
1478 : }
1479 : }
1480 0 : break;
1481 : }
1482 0 : default:
1483 0 : unexpected_message();
1484 : }
1485 0 : return false;
1486 : }
1487 :
1488 : #if UBLOX_RXM_RAW_LOGGING
1489 4870877 : if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAW && gps._raw_data != 0) {
1490 0 : log_rxm_raw(_buffer.rxm_raw);
1491 0 : return false;
1492 4870877 : } else if (_class == CLASS_RXM && _msg_id == MSG_RXM_RAWX && gps._raw_data != 0) {
1493 0 : log_rxm_rawx(_buffer.rxm_rawx);
1494 0 : return false;
1495 : }
1496 : #endif // UBLOX_RXM_RAW_LOGGING
1497 :
1498 : #if UBLOX_TIM_TM2_LOGGING
1499 4870877 : if ((_class == CLASS_TIM) && (_msg_id == MSG_TIM_TM2) && (_payload_length == 28)) {
1500 0 : log_tim_tm2();
1501 0 : return false;
1502 : }
1503 : #endif // UBLOX_TIM_TM2_LOGGING
1504 :
1505 4870877 : if (_class != CLASS_NAV) {
1506 0 : unexpected_message();
1507 0 : return false;
1508 : }
1509 :
1510 4870877 : switch (_msg_id) {
1511 694294 : case MSG_POSLLH:
1512 : Debug("MSG_POSLLH next_fix=%u", next_fix);
1513 694294 : if (havePvtMsg) {
1514 694294 : _unconfigured_messages |= CONFIG_RATE_POSLLH;
1515 694294 : break;
1516 : }
1517 0 : _check_new_itow(_buffer.posllh.itow);
1518 0 : _last_pos_time = _buffer.posllh.itow;
1519 0 : state.location.lng = _buffer.posllh.longitude;
1520 0 : state.location.lat = _buffer.posllh.latitude;
1521 0 : state.have_undulation = true;
1522 0 : state.undulation = (_buffer.posllh.altitude_msl - _buffer.posllh.altitude_ellipsoid) * 0.001;
1523 0 : set_alt_amsl_cm(state, _buffer.posllh.altitude_msl / 10);
1524 :
1525 0 : state.status = next_fix;
1526 0 : _new_position = true;
1527 0 : state.horizontal_accuracy = _buffer.posllh.horizontal_accuracy*1.0e-3f;
1528 0 : state.vertical_accuracy = _buffer.posllh.vertical_accuracy*1.0e-3f;
1529 0 : state.have_horizontal_accuracy = true;
1530 0 : state.have_vertical_accuracy = true;
1531 : #if UBLOX_FAKE_3DLOCK
1532 : state.location.lng = 1491652300L;
1533 : state.location.lat = -353632610L;
1534 : state.location.alt = 58400;
1535 : state.vertical_accuracy = 0;
1536 : state.horizontal_accuracy = 0;
1537 : #endif
1538 0 : break;
1539 695316 : case MSG_STATUS:
1540 : Debug("MSG_STATUS fix_status=%u fix_type=%u",
1541 : _buffer.status.fix_status,
1542 : _buffer.status.fix_type);
1543 695316 : _check_new_itow(_buffer.status.itow);
1544 695316 : if (havePvtMsg) {
1545 : // when we have PVT we don't need status, just change the rate for STATUS to zero
1546 694294 : _unconfigured_messages &= ~CONFIG_RATE_STATUS;
1547 694294 : _configure_message_rate(CLASS_NAV, _msg_id, 0);
1548 694294 : break;
1549 : }
1550 1022 : if (_buffer.status.fix_status & NAV_STATUS_FIX_VALID) {
1551 0 : if( (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) &&
1552 0 : (_buffer.status.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {
1553 0 : next_fix = AP_GPS_FixType::DGPS;
1554 0 : }else if( _buffer.status.fix_type == AP_GPS_UBLOX::FIX_3D) {
1555 0 : next_fix = AP_GPS_FixType::FIX_3D;
1556 0 : }else if (_buffer.status.fix_type == AP_GPS_UBLOX::FIX_2D) {
1557 0 : next_fix = AP_GPS_FixType::FIX_2D;
1558 : }else{
1559 0 : next_fix = AP_GPS_FixType::NONE;
1560 0 : state.status = AP_GPS_FixType::NONE;
1561 : }
1562 : }else{
1563 1022 : next_fix = AP_GPS_FixType::NONE;
1564 1022 : state.status = AP_GPS_FixType::NONE;
1565 : }
1566 : #if UBLOX_FAKE_3DLOCK
1567 : state.status = AP_GPS_FixType::FIX_3D;
1568 : next_fix = state.status;
1569 : #endif
1570 1022 : break;
1571 695319 : case MSG_DOP:
1572 : Debug("MSG_DOP");
1573 695319 : noReceivedHdop = false;
1574 695319 : _check_new_itow(_buffer.dop.itow);
1575 695319 : state.hdop = _buffer.dop.hDOP;
1576 695319 : state.vdop = _buffer.dop.vDOP;
1577 : #if UBLOX_FAKE_3DLOCK
1578 : state.hdop = 130;
1579 : state.hdop = 170;
1580 : #endif
1581 695319 : break;
1582 695319 : case MSG_SOL:
1583 : Debug("MSG_SOL fix_status=%u fix_type=%u",
1584 : _buffer.solution.fix_status,
1585 : _buffer.solution.fix_type);
1586 695319 : _check_new_itow(_buffer.solution.itow);
1587 695319 : if (havePvtMsg) {
1588 694297 : state.time_week = _buffer.solution.week;
1589 694297 : break;
1590 : }
1591 1022 : if (_buffer.solution.fix_status & NAV_STATUS_FIX_VALID) {
1592 1022 : if( (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) &&
1593 0 : (_buffer.solution.fix_status & AP_GPS_UBLOX::NAV_STATUS_DGPS_USED)) {
1594 0 : next_fix = AP_GPS_FixType::DGPS;
1595 1022 : }else if( _buffer.solution.fix_type == AP_GPS_UBLOX::FIX_3D) {
1596 0 : next_fix = AP_GPS_FixType::FIX_3D;
1597 1022 : }else if (_buffer.solution.fix_type == AP_GPS_UBLOX::FIX_2D) {
1598 0 : next_fix = AP_GPS_FixType::FIX_2D;
1599 : }else{
1600 1022 : next_fix = AP_GPS_FixType::NONE;
1601 1022 : state.status = AP_GPS_FixType::NONE;
1602 : }
1603 : }else{
1604 0 : next_fix = AP_GPS_FixType::NONE;
1605 0 : state.status = AP_GPS_FixType::NONE;
1606 : }
1607 1022 : if(noReceivedHdop) {
1608 1022 : state.hdop = _buffer.solution.position_DOP;
1609 : }
1610 1022 : state.num_sats = _buffer.solution.satellites;
1611 1022 : if (next_fix >= AP_GPS_FixType::FIX_2D) {
1612 0 : state.last_gps_time_ms = AP_HAL::millis();
1613 0 : state.time_week_ms = _buffer.solution.itow;
1614 0 : state.time_week = _buffer.solution.week;
1615 : }
1616 : #if UBLOX_FAKE_3DLOCK
1617 : next_fix = state.status;
1618 : state.num_sats = 10;
1619 : state.time_week = 1721;
1620 : state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;
1621 : state.last_gps_time_ms = AP_HAL::millis();
1622 : state.hdop = 130;
1623 : #endif
1624 1022 : break;
1625 :
1626 : #if GPS_MOVING_BASELINE
1627 4678 : case MSG_RELPOSNED:
1628 : {
1629 4678 : if (role != AP_GPS::GPS_ROLE_MB_ROVER) {
1630 : // ignore RELPOSNED if not configured as a rover
1631 2360 : break;
1632 : }
1633 : // note that we require the yaw to come from a fixed solution, not a float solution
1634 : // yaw from a float solution would only be acceptable with a very large separation between
1635 : // GPS modules
1636 2318 : const uint32_t valid_mask = static_cast<uint32_t>(RELPOSNED::relPosHeadingValid) |
1637 : static_cast<uint32_t>(RELPOSNED::relPosValid) |
1638 : static_cast<uint32_t>(RELPOSNED::gnssFixOK) |
1639 : static_cast<uint32_t>(RELPOSNED::isMoving) |
1640 : static_cast<uint32_t>(RELPOSNED::carrSolnFixed);
1641 2318 : const uint32_t invalid_mask = static_cast<uint32_t>(RELPOSNED::refPosMiss) |
1642 : static_cast<uint32_t>(RELPOSNED::refObsMiss) |
1643 : static_cast<uint32_t>(RELPOSNED::carrSolnFloat);
1644 :
1645 2318 : _check_new_itow(_buffer.relposned.iTOW);
1646 2318 : if (_buffer.relposned.iTOW != _last_relposned_itow+200) {
1647 : // useful for looking at packet loss on links
1648 : MB_Debug("RELPOSNED ITOW %u %u\n", unsigned(_buffer.relposned.iTOW), unsigned(_last_relposned_itow));
1649 : }
1650 2318 : _last_relposned_itow = _buffer.relposned.iTOW;
1651 : MB_Debug("RELPOSNED flags: %lx valid: %lx invalid: %lx\n", _buffer.relposned.flags, valid_mask, invalid_mask);
1652 2318 : if (((_buffer.relposned.flags & valid_mask) == valid_mask) &&
1653 2318 : ((_buffer.relposned.flags & invalid_mask) == 0)) {
1654 2318 : if (calculate_moving_base_yaw(_buffer.relposned.relPosHeading * 1e-5,
1655 2318 : _buffer.relposned.relPosLength * 0.01,
1656 2318 : _buffer.relposned.relPosD*0.01)) {
1657 2260 : state.have_gps_yaw_accuracy = true;
1658 2260 : state.gps_yaw_accuracy = _buffer.relposned.accHeading * 1e-5;
1659 2260 : _last_relposned_ms = AP_HAL::millis();
1660 : }
1661 2318 : state.relPosHeading = _buffer.relposned.relPosHeading * 1e-5;
1662 2318 : state.relPosLength = _buffer.relposned.relPosLength * 0.01;
1663 2318 : state.relPosD = _buffer.relposned.relPosD * 0.01;
1664 2318 : state.accHeading = _buffer.relposned.accHeading * 1e-5;
1665 2318 : state.relposheading_ts = AP_HAL::millis();
1666 : } else {
1667 0 : state.have_gps_yaw_accuracy = false;
1668 : }
1669 : }
1670 2318 : break;
1671 : #endif // GPS_MOVING_BASELINE
1672 :
1673 695319 : case MSG_PVT:
1674 : Debug("MSG_PVT");
1675 :
1676 695319 : havePvtMsg = true;
1677 :
1678 : // if we have PVT we don't want MSG_STATUS
1679 695319 : _unconfigured_messages &= ~CONFIG_RATE_STATUS;
1680 :
1681 : // position
1682 695319 : _check_new_itow(_buffer.pvt.itow);
1683 : // Only adjust if:
1684 : // we already have a valid week,
1685 : // PVT iTOW wrapped,
1686 : // and our time_week_ms still looks like end-of-week
1687 : // (meaning we haven't already accepted the rollover via TIMEGPS/SOL)
1688 695319 : if (state.time_week != 0 &&
1689 694297 : _last_pvt_itow != 0 &&
1690 694297 : _buffer.pvt.itow < _last_pvt_itow &&
1691 0 : state.time_week_ms > END_OF_WEEK_THRESHOLD_MS &&
1692 0 : _buffer.pvt.itow < START_OF_WEEK_THRESHOLD_MS) {
1693 0 : state.time_week++;
1694 : }
1695 :
1696 695319 : _last_pvt_itow = _buffer.pvt.itow;
1697 695319 : _last_pos_time = _buffer.pvt.itow;
1698 695319 : state.location.lng = _buffer.pvt.lon;
1699 695319 : state.location.lat = _buffer.pvt.lat;
1700 695319 : state.have_undulation = true;
1701 695319 : state.undulation = (_buffer.pvt.h_msl - _buffer.pvt.h_ellipsoid) * 0.001;
1702 695319 : set_alt_amsl_cm(state, _buffer.pvt.h_msl / 10);
1703 695319 : switch (_buffer.pvt.fix_type)
1704 : {
1705 7438 : case 0:
1706 7438 : state.status = AP_GPS_FixType::NONE;
1707 7438 : break;
1708 0 : case 1:
1709 0 : state.status = AP_GPS_FixType::NONE;
1710 0 : break;
1711 3 : case 2:
1712 3 : state.status = AP_GPS_FixType::FIX_2D;
1713 3 : break;
1714 687878 : case 3:
1715 687878 : state.status = AP_GPS_FixType::FIX_3D;
1716 687878 : if (_buffer.pvt.flags & 0b00000010) // diffsoln
1717 687875 : state.status = AP_GPS_FixType::DGPS;
1718 687878 : if (_buffer.pvt.flags & 0b01000000) // carrsoln - float
1719 3 : state.status = AP_GPS_FixType::RTK_FLOAT;
1720 687878 : if (_buffer.pvt.flags & 0b10000000) // carrsoln - fixed
1721 687869 : state.status = AP_GPS_FixType::RTK_FIXED;
1722 687878 : break;
1723 0 : case 4:
1724 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO,
1725 : "Unexpected state %d", _buffer.pvt.flags);
1726 0 : state.status = AP_GPS_FixType::FIX_3D;
1727 0 : break;
1728 0 : case 5:
1729 0 : state.status = AP_GPS_FixType::NONE;
1730 0 : break;
1731 0 : default:
1732 0 : state.status = AP_GPS_FixType::NONE;
1733 0 : break;
1734 : }
1735 695319 : next_fix = state.status;
1736 695319 : _new_position = true;
1737 695319 : state.horizontal_accuracy = _buffer.pvt.h_acc*1.0e-3f;
1738 695319 : state.vertical_accuracy = _buffer.pvt.v_acc*1.0e-3f;
1739 695319 : state.have_horizontal_accuracy = true;
1740 695319 : state.have_vertical_accuracy = true;
1741 : // SVs
1742 695319 : state.num_sats = _buffer.pvt.num_sv;
1743 : // velocity
1744 695319 : _last_vel_time = _buffer.pvt.itow;
1745 695319 : state.ground_speed = _buffer.pvt.gspeed*0.001f; // m/s
1746 695319 : state.ground_course = wrap_360(_buffer.pvt.head_mot * 1.0e-5f); // Heading 2D deg * 100000
1747 695319 : state.have_vertical_velocity = true;
1748 695319 : state.velocity.x = _buffer.pvt.velN * 0.001f;
1749 695319 : state.velocity.y = _buffer.pvt.velE * 0.001f;
1750 695319 : state.velocity.z = _buffer.pvt.velD * 0.001f;
1751 695319 : state.have_speed_accuracy = true;
1752 695319 : state.speed_accuracy = _buffer.pvt.s_acc*0.001f;
1753 695319 : _new_speed = true;
1754 : // dop
1755 695319 : if(noReceivedHdop) {
1756 0 : state.hdop = _buffer.pvt.p_dop;
1757 0 : state.vdop = _buffer.pvt.p_dop;
1758 : }
1759 :
1760 695319 : if (_buffer.pvt.fix_type >= 2) {
1761 687881 : state.last_gps_time_ms = AP_HAL::millis();
1762 : }
1763 :
1764 : // time
1765 695319 : state.time_week_ms = _buffer.pvt.itow;
1766 : #if UBLOX_FAKE_3DLOCK
1767 : state.location.lng = 1491652300L;
1768 : state.location.lat = -353632610L;
1769 : state.location.alt = 58400;
1770 : state.vertical_accuracy = 0;
1771 : state.horizontal_accuracy = 0;
1772 : state.status = AP_GPS_FixType::FIX_3D;
1773 : state.num_sats = 10;
1774 : state.time_week = 1721;
1775 : state.time_week_ms = AP_HAL::millis() + 3*60*60*1000 + 37000;
1776 : state.last_gps_time_ms = AP_HAL::millis();
1777 : state.hdop = 130;
1778 : state.speed_accuracy = 0;
1779 : next_fix = state.status;
1780 : #endif
1781 695319 : break;
1782 0 : case MSG_TIMEGPS:
1783 : Debug("MSG_TIMEGPS");
1784 0 : _check_new_itow(_buffer.timegps.itow);
1785 0 : if (_buffer.timegps.valid & UBX_TIMEGPS_VALID_WEEK_MASK) {
1786 0 : state.time_week_ms = _buffer.timegps.itow;
1787 0 : state.time_week = _buffer.timegps.week;
1788 0 : state.last_gps_time_ms = AP_HAL::millis();
1789 : }
1790 0 : break;
1791 695316 : case MSG_VELNED:
1792 : Debug("MSG_VELNED");
1793 695316 : if (havePvtMsg) {
1794 694294 : _unconfigured_messages |= CONFIG_RATE_VELNED;
1795 694294 : break;
1796 : }
1797 1022 : _check_new_itow(_buffer.velned.itow);
1798 1022 : _last_vel_time = _buffer.velned.itow;
1799 1022 : state.ground_speed = _buffer.velned.speed_2d*0.01f; // m/s
1800 1022 : state.ground_course = wrap_360(_buffer.velned.heading_2d * 1.0e-5f); // Heading 2D deg * 100000
1801 1022 : state.have_vertical_velocity = true;
1802 1022 : state.velocity.x = _buffer.velned.ned_north * 0.01f;
1803 1022 : state.velocity.y = _buffer.velned.ned_east * 0.01f;
1804 1022 : state.velocity.z = _buffer.velned.ned_down * 0.01f;
1805 1022 : velocity_to_speed_course(state);
1806 1022 : state.have_speed_accuracy = true;
1807 1022 : state.speed_accuracy = _buffer.velned.speed_accuracy*0.01f;
1808 : #if UBLOX_FAKE_3DLOCK
1809 : state.speed_accuracy = 0;
1810 : #endif
1811 1022 : _new_speed = true;
1812 1022 : break;
1813 695316 : case MSG_NAV_SVINFO:
1814 : {
1815 : Debug("MSG_NAV_SVINFO\n");
1816 : static const uint8_t HardwareGenerationMask = 0x07;
1817 695316 : _check_new_itow(_buffer.svinfo_header.itow);
1818 695316 : _hardware_generation = _buffer.svinfo_header.globalFlags & HardwareGenerationMask;
1819 695316 : switch (_hardware_generation) {
1820 0 : case UBLOX_5:
1821 : case UBLOX_6:
1822 : // only 7 and newer support CONFIG_GNSS
1823 0 : _unconfigured_messages &= ~CONFIG_GNSS;
1824 0 : break;
1825 695316 : case UBLOX_7:
1826 : case UBLOX_M8:
1827 : #if UBLOX_SPEED_CHANGE
1828 : port->begin(4000000U);
1829 : Debug("Changed speed to 4Mhz for SPI-driven UBlox\n");
1830 : #endif
1831 695316 : break;
1832 0 : default:
1833 0 : hal.console->printf("Wrong Ublox Hardware Version%u\n", _hardware_generation);
1834 0 : break;
1835 : };
1836 695316 : _unconfigured_messages &= ~CONFIG_VERSION;
1837 : /* We don't need that anymore */
1838 695316 : _configure_message_rate(CLASS_NAV, MSG_NAV_SVINFO, 0);
1839 695316 : break;
1840 : }
1841 0 : default:
1842 : Debug("Unexpected NAV message 0x%02x", (unsigned)_msg_id);
1843 0 : if (++_disable_counter == 0) {
1844 : Debug("Disabling NAV message 0x%02x", (unsigned)_msg_id);
1845 0 : _configure_message_rate(CLASS_NAV, _msg_id, 0);
1846 : }
1847 0 : return false;
1848 : }
1849 :
1850 4870877 : if (state.have_gps_yaw) {
1851 : // when we are a rover we want to ensure we have both the new
1852 : // PVT and the new RELPOSNED message so that we give a
1853 : // consistent view
1854 18038 : if (AP_HAL::millis() - _last_relposned_ms > 400) {
1855 : // we have stopped receiving valid RELPOSNED messages, disable yaw reporting
1856 0 : state.have_gps_yaw = false;
1857 18038 : } else if (_last_relposned_itow != _last_pvt_itow) {
1858 : // wait until ITOW matches
1859 2253 : return false;
1860 : }
1861 : }
1862 :
1863 : // we only return true when we get new position and speed data
1864 : // this ensures we don't use stale data
1865 4868624 : if (_new_position && _new_speed && _last_vel_time == _last_pos_time) {
1866 695319 : _new_speed = _new_position = false;
1867 695319 : return true;
1868 : }
1869 4173305 : return false;
1870 : }
1871 :
1872 : /*
1873 : * handle pps interrupt
1874 : */
1875 : #ifdef HAL_GPIO_PPS
1876 : void
1877 : AP_GPS_UBLOX::pps_interrupt(uint8_t pin, bool high, uint32_t timestamp_us)
1878 : {
1879 : _last_pps_time_us = AP_HAL::micros64();
1880 : }
1881 :
1882 : void
1883 : AP_GPS_UBLOX::set_pps_desired_freq(uint8_t freq)
1884 : {
1885 : _pps_freq = freq;
1886 : _unconfigured_messages |= CONFIG_TP5;
1887 : }
1888 : #endif
1889 :
1890 :
1891 : // UBlox auto configuration
1892 :
1893 : /*
1894 : * update checksum for a set of bytes
1895 : */
1896 : void
1897 5412742 : AP_GPS_UBLOX::_update_checksum(uint8_t *data, uint16_t len, uint8_t &ck_a, uint8_t &ck_b)
1898 : {
1899 20407056 : while (len--) {
1900 14994314 : ck_a += *data;
1901 14994314 : ck_b += ck_a;
1902 14994314 : data++;
1903 : }
1904 5412742 : }
1905 :
1906 :
1907 : /*
1908 : * send a ublox message
1909 : */
1910 : bool
1911 2706371 : AP_GPS_UBLOX::_send_message(uint8_t msg_class, uint8_t msg_id, const void *msg, uint16_t size)
1912 : {
1913 2706371 : if (port->txspace() < (sizeof(struct ubx_header) + 2 + size)) {
1914 0 : return false;
1915 : }
1916 : struct ubx_header header;
1917 2706371 : uint8_t ck_a=0, ck_b=0;
1918 2706371 : header.preamble1 = PREAMBLE1;
1919 2706371 : header.preamble2 = PREAMBLE2;
1920 2706371 : header.msg_class = msg_class;
1921 2706371 : header.msg_id = msg_id;
1922 2706371 : header.length = size;
1923 :
1924 2706371 : _update_checksum((uint8_t *)&header.msg_class, sizeof(header)-2, ck_a, ck_b);
1925 2706371 : _update_checksum((uint8_t *)msg, size, ck_a, ck_b);
1926 :
1927 2706371 : port->write((const uint8_t *)&header, sizeof(header));
1928 2706371 : port->write((const uint8_t *)msg, size);
1929 2706371 : port->write((const uint8_t *)&ck_a, 1);
1930 2706371 : port->write((const uint8_t *)&ck_b, 1);
1931 2706371 : return true;
1932 : }
1933 :
1934 : /*
1935 : * requests the given message rate for a specific message class
1936 : * and msg_id
1937 : * returns true if it sent the request, false if waiting on knowing the port
1938 : */
1939 : bool
1940 688141 : AP_GPS_UBLOX::_request_message_rate(uint8_t msg_class, uint8_t msg_id)
1941 : {
1942 : // Without knowing what communication port is being used it isn't possible to verify
1943 : // always ensure we have a port before sending the request
1944 688141 : if(_ublox_port >= UBLOX_MAX_PORTS) {
1945 688141 : _request_port();
1946 688141 : return false;
1947 : } else {
1948 : struct ubx_cfg_msg msg;
1949 0 : msg.msg_class = msg_class;
1950 0 : msg.msg_id = msg_id;
1951 0 : return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));
1952 : }
1953 : }
1954 :
1955 : /*
1956 : * configure a UBlox GPS for the given message rate for a specific
1957 : * message class and msg_id
1958 : */
1959 : bool
1960 1389610 : AP_GPS_UBLOX::_configure_message_rate(uint8_t msg_class, uint8_t msg_id, uint8_t rate)
1961 : {
1962 1389610 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(struct ubx_cfg_msg_rate)+2)) {
1963 0 : return false;
1964 : }
1965 :
1966 : struct ubx_cfg_msg_rate msg;
1967 1389610 : msg.msg_class = msg_class;
1968 1389610 : msg.msg_id = msg_id;
1969 1389610 : msg.rate = rate;
1970 1389610 : return _send_message(CLASS_CFG, MSG_CFG_MSG, &msg, sizeof(msg));
1971 : }
1972 :
1973 : /*
1974 : * configure F9/M10 based key/value pair - VALSET
1975 : */
1976 : bool
1977 0 : AP_GPS_UBLOX::_configure_valset(ConfigKey key, const void *value, uint8_t layers)
1978 : {
1979 0 : if (!supports_F9_config()) {
1980 0 : return false;
1981 : }
1982 :
1983 0 : const uint8_t len = config_key_size(key);
1984 0 : struct ubx_cfg_valset msg {};
1985 0 : uint8_t buf[sizeof(msg)+len];
1986 0 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {
1987 0 : return false;
1988 : }
1989 0 : msg.version = 1;
1990 0 : msg.layers = layers;
1991 0 : msg.transaction = 0;
1992 0 : msg.key = uint32_t(key);
1993 0 : memcpy(buf, &msg, sizeof(msg));
1994 0 : memcpy(&buf[sizeof(msg)], value, len);
1995 0 : auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, sizeof(buf));
1996 0 : return ret;
1997 0 : }
1998 :
1999 : /*
2000 : * configure F9 based key/value pair - VALGET
2001 : */
2002 : bool
2003 0 : AP_GPS_UBLOX::_configure_valget(ConfigKey key)
2004 : {
2005 0 : if (!supports_F9_config()) {
2006 0 : return false;
2007 : }
2008 : struct {
2009 : struct ubx_cfg_valget msg;
2010 : ConfigKey key;
2011 0 : } msg {};
2012 0 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(msg)+2)) {
2013 0 : return false;
2014 : }
2015 0 : msg.msg.version = 0;
2016 0 : msg.msg.layers = 0; // ram
2017 0 : msg.key = key;
2018 0 : return _send_message(CLASS_CFG, MSG_CFG_VALGET, &msg, sizeof(msg));
2019 : }
2020 :
2021 : /*
2022 : * configure F9 based key/value pair for a complete configuration set
2023 : *
2024 : * this method requests each configuration variable from the GPS.
2025 : * When we handle the reply in _parse_gps we may then choose to set a
2026 : * MSG_CFG_VALSET back to the GPS if we don't like its response.
2027 : */
2028 : bool
2029 0 : AP_GPS_UBLOX::_configure_config_set(const config_list *list, uint8_t count, uint32_t unconfig_bit, uint8_t layers)
2030 : {
2031 0 : active_config.list = list;
2032 0 : active_config.count = count;
2033 0 : active_config.done_mask = 0;
2034 0 : active_config.unconfig_bit = unconfig_bit;
2035 0 : active_config.layers = layers;
2036 : // we start by fetching multiple values at once (with fetch_index
2037 : // -1) then if we get a NACK for VALGET we switch to fetching one
2038 : // value at a time. This copes with the M10S which can only fetch
2039 : // one value at a time
2040 0 : active_config.fetch_index = use_single_valget? 0 :-1;
2041 :
2042 0 : uint8_t buf[sizeof(ubx_cfg_valget)+count*sizeof(ConfigKey)];
2043 0 : struct ubx_cfg_valget msg {};
2044 0 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {
2045 0 : return false;
2046 : }
2047 0 : msg.version = 0;
2048 0 : msg.layers = 0; // ram
2049 0 : memcpy(buf, &msg, sizeof(msg));
2050 0 : for (uint8_t i=0; i<count; i++) {
2051 0 : memcpy(&buf[sizeof(msg)+i*sizeof(ConfigKey)], &list[i].key, sizeof(ConfigKey));
2052 : }
2053 0 : return _send_message(CLASS_CFG, MSG_CFG_VALGET, buf, sizeof(buf));
2054 0 : }
2055 :
2056 : /*
2057 : * configure F9 based key/value pair for a complete configuration set
2058 : *
2059 : * this method sends all the key/value pairs in a block, but makes no attempt to check
2060 : * the results. Sending in a block is necessary for updates such as GNSS where certain
2061 : * combinations are invalid and setting one at a time will not produce the correct result
2062 : * if the result needs to be validated then a subsequent _configure_config_set() can be
2063 : * issued which will get all the values and reset those that are not properly updated.
2064 : */
2065 : bool
2066 0 : AP_GPS_UBLOX::_configure_list_valset(const config_list *list, uint8_t count, uint8_t layers)
2067 : {
2068 0 : if (!supports_F9_config()) {
2069 0 : return false;
2070 : }
2071 :
2072 0 : struct ubx_cfg_valset msg {};
2073 0 : uint8_t buf[sizeof(msg)+sizeof(config_list)*count];
2074 0 : if (port->txspace() < (uint16_t)(sizeof(struct ubx_header)+sizeof(buf)+2)) {
2075 0 : return false;
2076 : }
2077 0 : msg.version = 1;
2078 0 : msg.layers = layers;
2079 0 : msg.transaction = 0;
2080 0 : uint32_t msg_len = sizeof(msg) - sizeof(msg.key);
2081 0 : memcpy(buf, &msg, msg_len);
2082 :
2083 0 : uint8_t* payload = &buf[msg_len];
2084 0 : for (uint8_t i=0; i<count; i++) {
2085 0 : const uint8_t len = config_key_size(list[i].key);
2086 0 : memcpy(payload, &list[i].key, sizeof(ConfigKey));
2087 0 : payload += sizeof(ConfigKey);
2088 0 : msg_len += sizeof(ConfigKey);
2089 0 : memcpy(payload, &list[i].value, len);
2090 0 : payload += len;
2091 0 : msg_len += len;
2092 : }
2093 :
2094 0 : auto ret = _send_message(CLASS_CFG, MSG_CFG_VALSET, buf, msg_len);
2095 0 : return ret;
2096 0 : }
2097 :
2098 : /*
2099 : * save gps configurations to non-volatile memory sent until the call of
2100 : * this message
2101 : */
2102 : void
2103 0 : AP_GPS_UBLOX::_save_cfg()
2104 : {
2105 : static const ubx_cfg_cfg save_cfg {
2106 : clearMask: 0,
2107 : saveMask: SAVE_CFG_ALL,
2108 : loadMask: 0
2109 : };
2110 0 : _send_message(CLASS_CFG, MSG_CFG_CFG, &save_cfg, sizeof(save_cfg));
2111 0 : _last_cfg_sent_time = AP_HAL::millis();
2112 0 : _num_cfg_save_tries++;
2113 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO,
2114 : "GPS %d: u-blox saving config",
2115 : state.instance + 1);
2116 0 : }
2117 :
2118 : /*
2119 : detect a Ublox GPS. Adds one byte, and returns true if the stream
2120 : matches a UBlox
2121 : */
2122 : bool
2123 36828 : AP_GPS_UBLOX::_detect(struct UBLOX_detect_state &state, uint8_t data)
2124 : {
2125 36828 : reset:
2126 36828 : switch (state.step) {
2127 1023 : case 1:
2128 1023 : if (PREAMBLE2 == data) {
2129 1023 : state.step++;
2130 1023 : break;
2131 : }
2132 0 : state.step = 0;
2133 : FALLTHROUGH;
2134 1023 : case 0:
2135 1023 : if (PREAMBLE1 == data)
2136 1023 : state.step++;
2137 1023 : break;
2138 1023 : case 2:
2139 1023 : state.step++;
2140 1023 : state.ck_b = state.ck_a = data;
2141 1023 : break;
2142 1023 : case 3:
2143 1023 : state.step++;
2144 1023 : state.ck_b += (state.ck_a += data);
2145 1023 : break;
2146 1023 : case 4:
2147 1023 : state.step++;
2148 1023 : state.ck_b += (state.ck_a += data);
2149 1023 : state.payload_length = data;
2150 1023 : break;
2151 1023 : case 5:
2152 1023 : state.step++;
2153 1023 : state.ck_b += (state.ck_a += data);
2154 1023 : state.payload_counter = 0;
2155 1023 : break;
2156 28644 : case 6:
2157 28644 : state.ck_b += (state.ck_a += data);
2158 28644 : if (++state.payload_counter == state.payload_length)
2159 1023 : state.step++;
2160 28644 : break;
2161 1023 : case 7:
2162 1023 : state.step++;
2163 1023 : if (state.ck_a != data) {
2164 0 : state.step = 0;
2165 0 : goto reset;
2166 : }
2167 1023 : break;
2168 1023 : case 8:
2169 1023 : state.step = 0;
2170 1023 : if (state.ck_b == data) {
2171 : // a valid UBlox packet
2172 1023 : return true;
2173 : } else {
2174 0 : goto reset;
2175 : }
2176 : }
2177 35805 : return false;
2178 : }
2179 :
2180 : void
2181 628620 : AP_GPS_UBLOX::_request_version(void)
2182 : {
2183 628620 : _send_message(CLASS_MON, MSG_MON_VER, nullptr, 0);
2184 628620 : }
2185 :
2186 : void
2187 0 : AP_GPS_UBLOX::_configure_rate(void)
2188 : {
2189 : struct ubx_cfg_nav_rate msg;
2190 : // require a minimum measurement rate of 5Hz
2191 0 : msg.measure_rate_ms = gps.get_rate_ms(state.instance);
2192 0 : msg.nav_rate = 1;
2193 0 : msg.timeref = 0; // UTC time
2194 0 : _send_message(CLASS_CFG, MSG_CFG_RATE, &msg, sizeof(msg));
2195 0 : }
2196 :
2197 : static const char *reasons[] = {"navigation rate",
2198 : "posllh rate",
2199 : "status rate",
2200 : "solution rate",
2201 : "velned rate",
2202 : "dop rate",
2203 : "hw monitor rate",
2204 : "hw2 monitor rate",
2205 : "raw rate",
2206 : "version",
2207 : "navigation settings",
2208 : "GNSS settings",
2209 : "SBAS settings",
2210 : "PVT rate",
2211 : "time pulse settings",
2212 : "TIMEGPS rate",
2213 : "Time mode settings",
2214 : "RTK MB",
2215 : "TIM TM2",
2216 : "F9",
2217 : "M10",
2218 : "L5 Enable Disable"};
2219 :
2220 : static_assert((1 << ARRAY_SIZE(reasons)) == CONFIG_LAST, "UBLOX: Missing configuration description");
2221 :
2222 : void
2223 0 : AP_GPS_UBLOX::broadcast_configuration_failure_reason(void) const {
2224 0 : for (uint8_t i = 0; i < ARRAY_SIZE(reasons); i++) {
2225 0 : if (_unconfigured_messages & (1 << i)) {
2226 0 : GCS_SEND_TEXT(MAV_SEVERITY_INFO, "GPS %u: u-blox %s configuration 0x%02x",
2227 : (unsigned int)(state.instance + 1), reasons[i], (unsigned int)_unconfigured_messages);
2228 0 : break;
2229 : }
2230 : }
2231 0 : }
2232 :
2233 : /*
2234 : return velocity lag in seconds
2235 : */
2236 78073201 : bool AP_GPS_UBLOX::get_lag(float &lag_sec) const
2237 : {
2238 78073201 : switch (_hardware_generation) {
2239 6636 : case UBLOX_UNKNOWN_HARDWARE_GENERATION:
2240 6636 : lag_sec = 0.22f;
2241 : // always bail out in this case, it's used to indicate we have yet to receive a valid
2242 : // hardware generation, however the user may have inhibited us detecting the generation
2243 : // so if we aren't allowed to do configuration, we will accept this as the default delay
2244 6636 : return gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE;
2245 0 : case UBLOX_5:
2246 : case UBLOX_6:
2247 : default:
2248 0 : lag_sec = 0.22f;
2249 0 : break;
2250 78066565 : case UBLOX_7:
2251 : case UBLOX_M8:
2252 : // based on flight logs the 7 and 8 series seem to produce about 120ms lag
2253 78066565 : lag_sec = 0.12f;
2254 78066565 : break;
2255 0 : case UBLOX_F9:
2256 : case UBLOX_M9:
2257 : case UBLOX_M10:
2258 : #if AP_GPS_UBLOX_CFGV2_ENABLED
2259 : case UBLOX_F10:
2260 : case UBLOX_F20:
2261 : case UBLOX_X20:
2262 : #endif
2263 : // F9 lag not verified yet from flight log, but likely to be at least
2264 : // as good as M8
2265 0 : lag_sec = 0.12f;
2266 : #if GPS_MOVING_BASELINE
2267 0 : if (role == AP_GPS::GPS_ROLE_MB_BASE ||
2268 0 : role == AP_GPS::GPS_ROLE_MB_ROVER) {
2269 : // the moving baseline rover will lag about 40ms from the
2270 : // base. We need to provide the more conservative value to
2271 : // ensure that the EKF allocates a larger enough buffer
2272 0 : lag_sec += 0.04;
2273 : }
2274 : #endif
2275 0 : break;
2276 : };
2277 78066565 : return true;
2278 : }
2279 :
2280 : #if HAL_LOGGING_ENABLED
2281 1776 : void AP_GPS_UBLOX::Write_AP_Logger_Log_Startup_messages() const
2282 : {
2283 1776 : AP_GPS_Backend::Write_AP_Logger_Log_Startup_messages();
2284 :
2285 1776 : if (_have_version) {
2286 0 : AP::logger().Write_MessageF("u-blox %d HW: %s SW: %s",
2287 0 : state.instance+1,
2288 0 : _version.hwVersion,
2289 0 : _version.swVersion);
2290 : }
2291 1776 : }
2292 : #endif
2293 :
2294 : // uBlox specific check_new_itow(), handling message length
2295 3479929 : void AP_GPS_UBLOX::_check_new_itow(uint32_t itow)
2296 : {
2297 3479929 : check_new_itow(itow, _payload_length + sizeof(ubx_header) + 2);
2298 3479929 : }
2299 :
2300 : // support for retrieving RTCMv3 data from a moving baseline base
2301 22030 : bool AP_GPS_UBLOX::get_RTCMV3(const uint8_t *&bytes, uint16_t &len)
2302 : {
2303 : #if GPS_MOVING_BASELINE
2304 22030 : if (rtcm3_parser) {
2305 21896 : len = rtcm3_parser->get_len(bytes);
2306 21896 : return len > 0;
2307 : }
2308 : #endif
2309 134 : return false;
2310 : }
2311 :
2312 : // clear previous RTCM3 packet
2313 0 : void AP_GPS_UBLOX::clear_RTCMV3(void)
2314 : {
2315 : #if GPS_MOVING_BASELINE
2316 0 : if (rtcm3_parser) {
2317 0 : rtcm3_parser->clear_packet();
2318 : }
2319 : #endif
2320 0 : }
2321 :
2322 : // ublox specific healthy checks
2323 1062646 : bool AP_GPS_UBLOX::is_healthy(void) const
2324 : {
2325 : #if CONFIG_HAL_BOARD == HAL_BOARD_SITL
2326 1062646 : if (gps._auto_config == AP_GPS::GPS_AUTO_CONFIG_DISABLE) {
2327 : // allow for fake ublox moving baseline
2328 2341 : return true;
2329 : }
2330 : #endif
2331 : #if GPS_MOVING_BASELINE
2332 3180870 : if ((role == AP_GPS::GPS_ROLE_MB_BASE ||
2333 1060350 : role == AP_GPS::GPS_ROLE_MB_ROVER) &&
2334 45 : !supports_F9_config()) {
2335 : // need F9 or above for moving baseline
2336 45 : return false;
2337 : }
2338 1060260 : if (role == AP_GPS::GPS_ROLE_MB_BASE && rtcm3_parser == nullptr && !mb_use_uart2()) {
2339 : // we haven't initialised RTCMv3 parser
2340 0 : return false;
2341 : }
2342 : #endif
2343 1060260 : return true;
2344 : }
2345 :
2346 : // populate config_GNSS with F9 GNSS configuration
2347 0 : uint8_t AP_GPS_UBLOX::populate_F9_gnss(void)
2348 : {
2349 0 : uint8_t cfg_count = 0;
2350 :
2351 0 : if (params.gnss_mode == 0) {
2352 0 : _unconfigured_messages &= ~CONFIG_F9;
2353 0 : last_configured_gnss = params.gnss_mode;
2354 0 : return 0;
2355 : }
2356 :
2357 0 : if ((_unconfigured_messages & CONFIG_F9) != 0) {
2358 : // ZED-F9P defaults are
2359 : // GPS L1C/A+L2C(ZED)
2360 : // SBAS L1C/A
2361 : // GALILEO E1+E5B(ZED)+E5A(NEO)
2362 : // BEIDOU B1+B2(ZED)+B2A(NEO)
2363 : // QZSS L1C/A+L2C(ZED)+L5(NEO)
2364 : // GLONASS L1+L2(ZED)
2365 : // IMES not supported
2366 : // GPS and QZSS should be enabled/disabled together, but we will leave them alone
2367 : // QZSS and SBAS can only be enabled if GPS is enabled
2368 :
2369 0 : if (config_GNSS == nullptr) {
2370 0 : config_GNSS = (config_list*)calloc(UBLOX_MAX_GNSS_CONFIG_BLOCKS*3, sizeof(config_list));
2371 : }
2372 :
2373 0 : if (config_GNSS == nullptr) {
2374 0 : return 0;
2375 : }
2376 :
2377 0 : uint8_t gnss_mode = params.gnss_mode;
2378 0 : gnss_mode |= 1U<<GNSS_GPS;
2379 0 : gnss_mode |= 1U<<GNSS_QZSS;
2380 0 : gnss_mode &= ~(1U<<GNSS_IMES);
2381 0 : params.gnss_mode.set_and_save(gnss_mode);
2382 :
2383 0 : for(int i = 0; i < UBLOX_MAX_GNSS_CONFIG_BLOCKS; i++) {
2384 0 : bool ena = gnss_mode & (1U<<i);
2385 0 : switch (i) {
2386 0 : case GNSS_SBAS:
2387 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_ENA, ena };
2388 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_SBAS_L1CA_ENA, ena };
2389 0 : break;
2390 0 : case GNSS_GAL:
2391 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_ENA, ena };
2392 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E1_ENA, ena };
2393 0 : if (_hardware_variant == UBLOX_F9_ZED) {
2394 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5B_ENA, ena };
2395 : } else {
2396 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GAL_E5A_ENA, ena };
2397 : }
2398 0 : break;
2399 0 : case GNSS_BDS:
2400 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_ENA, ena };
2401 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B1_ENA, ena };
2402 0 : if (_hardware_variant == UBLOX_F9_ZED) {
2403 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2_ENA, ena };
2404 : } else {
2405 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_BDS_B2A_ENA, ena };
2406 : }
2407 0 : break;
2408 0 : case GNSS_GLO:
2409 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_ENA, ena };
2410 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L1_ENA, ena };
2411 0 : if (_hardware_variant == UBLOX_F9_ZED) {
2412 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_GLO_L2_ENA, ena };
2413 : }
2414 0 : break;
2415 0 : case GNSS_NAVIC:
2416 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_NAVIC_ENA, ena };
2417 0 : config_GNSS[cfg_count++] = { ConfigKey::CFG_SIGNAL_NAVIC_L5_ENA, ena };
2418 0 : break;
2419 : // not supported or leave alone
2420 0 : case GNSS_IMES:
2421 : case GNSS_QZSS:
2422 : case GNSS_GPS:
2423 0 : break;
2424 : }
2425 : }
2426 : }
2427 :
2428 0 : last_configured_gnss = params.gnss_mode;
2429 :
2430 0 : return cfg_count;
2431 : }
2432 :
2433 : // return true if GPS is capable of F9 config
2434 45 : bool AP_GPS_UBLOX::supports_F9_config(void) const
2435 : {
2436 45 : return _hardware_generation == UBLOX_F9 || _hardware_generation == UBLOX_M10
2437 : #if AP_GPS_UBLOX_CFGV2_ENABLED
2438 90 : || _hardware_generation == UBLOX_F20 || _hardware_generation == UBLOX_X20
2439 : #endif
2440 : ;
2441 : }
2442 :
2443 : // return true if GPS is capable of F9 config
2444 0 : bool AP_GPS_UBLOX::is_gnss_key(ConfigKey key) const
2445 : {
2446 0 : return (unsigned(key) & 0xFFFF0000) == 0x10310000;
2447 : }
2448 :
2449 : #endif
|