Coverage Report

Created: 2026-07-16 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_ubx.c
Line
Count
Source
1
/*
2
 * UBX driver.  For u-blox binary, also includes Antaris4 binary
3
 * Reference manuals are at
4
 * http://www.u-blox.com/en/download/documents-a-resources/u-blox-6-gps-modules-resources.html
5
 *
6
 * updated for u-blox 8
7
 * http://www.ublox.com/images/downloads/Product_Docs/u-bloxM8_ReceiverDescriptionProtocolSpec_%28UBX-13003221%29_Public.pdf
8
 *
9
 * Week counters are not limited to 10 bits. It's unknown what
10
 * the firmware is doing to disambiguate them, if anything; it might just
11
 * be adding a fixed offset based on a hidden epoch value, in which case
12
 * unhappy things will occur on the next rollover.
13
 *
14
 * For the Antaris 4, the default leap-second offset (before getting one from
15
 * the sats, one presumes) is 0sec; for the u-blox 6 it's 15sec.
16
 *
17
 * This file is Copyright by the GPSD project
18
 * SPDX-License-Identifier: BSD-2-clause
19
 *
20
 */
21
22
#include "../include/gpsd_config.h"  // must be before all includes
23
24
#include <math.h>
25
#include <stdbool.h>
26
#include <stdio.h>
27
#include <stdlib.h>                // for abs()
28
#include <string.h>
29
#include <unistd.h>
30
31
32
#include "../include/compiler.h"   // for FALLTHROUGH
33
#include "../include/gpsd.h"
34
#include "../include/driver_ubx.h"
35
36
#include "../include/bits.h"       // For UINT2INT()
37
#include "../include/timespec.h"
38
39
// UBX-NAV-PVT, UBX-NAV-PVAT flag bits
40
#define UBX_NAV_PVT_FLAG_GPS_FIX_OK 0x01
41
1.86k
#define UBX_NAV_PVT_FLAG_DGPS       0x02
42
#define UBX_NAV_PVT_FLAG_ROLL_OK    0x08
43
#define UBX_NAV_PVT_FLAG_PITCH_OK   0x10
44
213
#define UBX_NAV_PVT_FLAG_HDG_OK     0x20
45
460
#define UBX_NAV_PVT_FLAG_RTK_FLT    0x40
46
922
#define UBX_NAV_PVT_FLAG_RTK_FIX    0x80
47
48
/*
49
 * Some high-precision messages provide data where the main part is a
50
 * signed 32-bit integer (same as the standard-precision versions),
51
 * and there's an 8-bit signed field providing an addend scaled to
52
 * 1/100th of the main value.  This macro provides a fetch for such
53
 * values, scaled to match the extension (i.e., 100X the main-value scale).
54
 * Since the fields are nonconsective, the offsets are provided separately.
55
 * The result is a signed 64-bit integer.
56
 *
57
 * The second macro incorporates scaling the result by a specified double.
58
 */
59
#define getles32x100s8(buf, off, offx) \
60
420
    ((int64_t)(getles32((buf), (off)) * 100LL + getsb((buf), (offx))))
61
#define getles32x100s8d(buf, off, offx, scale) \
62
420
    (getles32x100s8((buf), (off), (offx)) * (double)(scale))
63
64
/*
65
 * A ubx packet looks like this:
66
 * leader: 0xb5 0x62
67
 * message class: 1 byte
68
 * message type: 1 byte
69
 * length of payload: 2 bytes
70
 * payload: variable length
71
 * checksum: 2 bytes
72
 *
73
 * see also the FV25 and UBX documents on reference.html
74
 */
75
29.1k
#define UBX_PREFIX_LEN          6
76
#define UBX_CLASS_OFFSET        2
77
#define UBX_TYPE_OFFSET         3
78
79
// because we hates magic numbers forever
80
132
#define USART1_ID               1
81
#define USART2_ID               2
82
0
#define USB_ID                  3
83
508
#define UBX_PROTOCOL_MASK       0x01
84
508
#define NMEA_PROTOCOL_MASK      0x02
85
254
#define RTCM_PROTOCOL_MASK      0x04
86
508
#define RTCM3_PROTOCOL_MASK     0x20    // protVer 20+
87
254
#define UBX_CFG_LEN             20
88
254
#define outProtoMask            14
89
90
// UBX Satellite/Dignal Numbering
91
static const struct vlist_t vgnss_sig_ids[] = {
92
    {0x0000, "GPS L1 C/A"},
93
    {0x0003, "GPS L2 CL"},
94
    {0x0004, "GPS L2 CM"},
95
    {0x0006, "GPS L5 I"},
96
    {0x0007, "GPS L5 Q"},
97
    {0x0100, "SBAS L1 C/A"},
98
    {0x0200, "GAL E1 C"},
99
    {0x0201, "GAL E1 B"},
100
    {0x0203, "GAL E5 aI"},
101
    {0x0204, "GAL E5 aQ"},
102
    {0x0205, "GAL E5 bI"},
103
    {0x0206, "GAL E5 bQ"},
104
    {0x0208, "GAL E6 B"},
105
    {0x0209, "GAL E6 C"},
106
    {0x020A, "GAL E6 A"},
107
    {0x0300, "BDS B1I D1"},
108
    {0x0301, "BDS B1I D2"},
109
    {0x0302, "BDS B2I D1"},
110
    {0x0303, "BDS B2I D2"},
111
    {0x0304, "BDS B3I D1"},
112
    {0x0305, "BDS B1 Cp"},
113
    {0x0306, "BDS B1 Cd"},
114
    {0x0307, "BDS B2 ap"},
115
    {0x0308, "BDS B2 ad"},
116
    {0x030A, "BDS B3I D2"},
117
    {0x0500, "QZSS L1 C/A"},
118
    {0x0501, "QZSS L1 S"},
119
    {0x0504, "QZSS L2 CM"},
120
    {0x0505, "QZSS L2 CL"},
121
    {0x0508, "QZSS L5 I"},
122
    {0x0509, "QZSS L5 Q"},
123
    {0x050C, "QZSS L1 C/B"},
124
    {0x0600, "GLO L1 OF"},
125
    {0x0602, "GLO L2 OF"},
126
    {0x0700, "NavIc L5 A"},
127
    {0, NULL},
128
};
129
130
// UBX-ACK-* ids
131
static const struct vlist_t vack_ids[] = {
132
    {UBX_ACK_ACK, "ACK-ACK"},
133
    {UBX_ACK_NAK, "ACK-NAK"},
134
    {0, NULL},
135
};
136
137
// UBX-INF-* inf_ids
138
static const struct vlist_t vinf_ids[] = {
139
    {UBX_INF_DEBUG, "INF-DEBUG"},
140
    {UBX_INF_TEST, "INF-TEST"},
141
    {UBX_INF_NOTICE, "INF-NOTICE"},
142
    {UBX_INF_WARNING, "INF-WARNING"},
143
    {UBX_INF_ERROR, " INF-ERROR"},
144
    {0, NULL},
145
};
146
147
// UBX-MON-COMMS protIds
148
static const struct vlist_t vprotIds[] = {
149
    {0, "UBX"},
150
    {1, "NMEA"},
151
    {2, "RTCM2"},
152
    {5, "RTCM3"},
153
    {255, "None"},
154
    {0, NULL},
155
};
156
157
// UBX-MON-COMMS txErrors
158
static const struct flist_t vmon_comms_txerrors[] = {
159
    {1, 1, "mem"},
160
    {2, 2, "alloc"},
161
    {0, 0, NULL},
162
};
163
164
// UBX-MON-TXBUF errors
165
static const struct flist_t vmon_txbuf_errors[] = {
166
    {0x40, 0x40, "mem"},
167
    {0x80, 0x80, "alloc"},
168
    {0, 0, NULL},
169
};
170
171
// UBX-MON-HW flags
172
static const struct flist_t vmon_hw_flags[] = {
173
    {1, 1, "RTC Calibrated"},
174
    {2, 2, "Safeboot Active"},
175
    {0x04, 0x0c, "Jam OK"},
176
    {0x08, 0x0c, "Jam Warn"},
177
    {0x0c, 0x0c, "Jam Critical"},
178
    {0x10, 0x10, "xtal Absent"},
179
    {0, 0, NULL},
180
};
181
182
// UBX-MON-HW aPower
183
static const struct vlist_t vaPower[] = {
184
    {0, "Off"},
185
    {1, "On"},
186
    {2, "Unk"},
187
    {0, NULL},
188
};
189
190
// UBX-MON-HW aStatus
191
static const struct vlist_t vaStatus[] = {
192
    {0, "Init"},
193
    {1, "Unk"},
194
    {2, "OK"},
195
    {3, "Short"},
196
    {4, "Open"},
197
    {0, NULL},
198
};
199
200
// UBX-MON-RF blockId
201
static const struct vlist_t vmon_rf_blockId[] = {
202
    {0, "L1 Unk"},
203
    {1, "L2 or L5"},
204
    {0, NULL},
205
};
206
207
// UBX-MON-RF flags
208
static const struct vlist_t vmon_rf_flags[] = {
209
    {0, "Jam Unk"},
210
    {1, "Jam OK"},
211
    {2, "Jam Warn"},
212
    {3, "Jam Crit"},
213
    {0, NULL},
214
};
215
216
// Names for portID values in:
217
//  UBX-CFG-PRT, UBX-MON-IO, UBX-MON-RXBUF, UBX-MON-TXBUF, target
218
static const struct vlist_t vtarget[] = {
219
    {0, "DDC"},             // The license free name for I2C
220
    {1, "UART1"},
221
    {2, "UART2"},
222
    {3, "USB"},
223
    {4, "SPI"},
224
    {0x100, "UART1"},       // MON-COMMS
225
    {0x200, "UART2"},       // MON-COMMS
226
    {0x300, "USB"},         // MON-COMMS
227
    {0x400, "SPI"},         // MON-COMMS
228
    {0, NULL},
229
};
230
231
// UBX-HNR-PVT, UBX-NAV-SOL gpsFix, UBX-NAV-PVT fixType
232
static const struct vlist_t vpvt_fixType[] = {
233
    {0, "None"},
234
    {1, "DR"},
235
    {2, "2D"},
236
    {3, "3D"},
237
    {4, "GNSSDR"},
238
    {5, "Time"},
239
    {0, NULL},
240
};
241
242
// UBX-HNR-PVT flags
243
static const struct flist_t fhnr_pvt_flags[] = {
244
    {1, 1, "gnssFixOK"},
245
    {2, 2, "diffSoln"},
246
    {4, 4, "WKNSET"},
247
    {8, 8, "TOWSET"},
248
    {0x20, 0x20, "headVehValid"},
249
    {0, 0, NULL},
250
};
251
252
// UBX-NAV-PVT flags
253
static const struct flist_t fnav_pvt_flags[] = {
254
    {1, 1, "gnssFixOK"},
255
    {2, 2, "diffSoln"},
256
    {8, 8, "vehRollValid"},
257
    // {0, 0x10, "psmState"},      // ??
258
    {0x10, 0x10, "vehPitchValid"},
259
    {0x10, 0x10, "vehHeadingValid"},  // aka headVelValid
260
    {0x40, 0xc0, "CarrSolnFLT"},
261
    {0x80, 0xc0, "CarrSolnFIX"},
262
    {0, 0, NULL},
263
};
264
265
// UBX-NAV-PVT flags2
266
static const struct flist_t fpvt_flags2[] = {
267
    {0x20, 0x20, "confirmedAvai"},   // protver 19+
268
    {0x40, 0x40, "confirmedDate"},
269
    {0x80, 0x80, "confirmedTime"},
270
    {0, 0, NULL},
271
};
272
273
// UBX-NAV-PVT flags3
274
static const struct flist_t fpvt_flags3[] = {
275
    {0x20, 0x20, "invalLlh"},
276
    {0, 0, NULL},
277
};
278
279
// UBX-HNR-PVT, UBX-NAV-PVT valid
280
static const struct flist_t fpvt_valid[] = {
281
    {1, 1, "validDate"},
282
    {2, 2, "validTime"},
283
    {4, 4, "fullyResolved"},
284
    {8, 8, "validMag"},
285
    {0, 0, NULL},
286
};
287
288
// UBX-NAV-PVT, dgps_age
289
static const int pvt_dgps_age[] = {
290
    -1, 1, 2, 5, 10,
291
    15, 20, 30, 45, 60,
292
    90, 120, 240};
293
294
// UBX-NAV-SAT flags
295
static const struct flist_t fsat_flags[] = {
296
    // bits 0, 1, and 2 == qualityInd
297
    {8, 8, "Used"},
298
    {0x10, 0x30, "healthy"},
299
    {0x20, 0x30, "unhealthy"},
300
    {0x40, 0x40, "diffCorr"},
301
    {0x800, 0x800, "ephAvail"},
302
    {0x1000, 0x1000, "almAvail"},
303
    {0x2000, 0x2000, "anoAvail"},
304
    {0x4000, 0x4000, "aopAvail"},
305
    {0x10000, 0x10000, "sbasCorrUsed"},
306
    {0x20000, 0x20000, "rtcmCorrUsed"},
307
    {0x40000, 0x40000, "slasCorrUsed"},
308
    {0x80000, 0x80000, "spartnCorrUsed"},
309
    {0x100000L, 0x100000L, "prCorrUsed"},
310
    {0x200000L, 0x200000L, "crCorrUsed"},
311
    {0x400000L, 0x400000L, "doCorrUsed"},
312
    {0x800000L, 0x800000L, "cbasCorrUsed"},
313
    {0, 0, NULL},
314
};
315
316
// UBX-NAV-SIG corrSource
317
static const struct vlist_t vsig_corrsource[] = {
318
    {0, "None"},
319
    {1, "SBAS"},
320
    {2, "rBDS"},
321
    {3, "RTCM2"},
322
    {4, "RTCM3 OSR"},
323
    {5, "RTCM3 SSR"},
324
    {6, "QZSS SLAS"},
325
    {7, "SPARTN"},
326
    {8, "CLAS"},
327
    {0, NULL},
328
};
329
330
// UBX-NAV-SIG ionoModel
331
static const struct vlist_t vsig_ionomodel[] = {
332
    {0, "None"},
333
    {1, "Klobuchar GPS"},
334
    {2, "SBAS"},
335
    {8, "Dual F Delay"},
336
    {0, NULL},
337
};
338
339
// UBX-NAV-SIG sigFlags
340
static const struct flist_t fsig_sigFlags[] = {
341
    {1, 3, "healthy"},
342
    {2, 3, "unhealthy"},
343
    {4, 4, "prSmoothed"},
344
    {8, 8, "prUsed"},
345
    {0x10, 0x10, "crUsed"},
346
    {0x20, 0x20, "doUsed"},
347
    {0x40, 0x40, "prCorrUsed"},
348
    {0x80, 0x80, "crCorrUsed"},
349
    {0x100, 0x100, "doCorrUsed"},
350
    {0x200, 0x200, "Authenticated"},  // u-blox M9 SPG, GALILEO
351
    {0, 0, NULL},
352
};
353
354
// UBX-NAV-SVIN active
355
// UBX-TIM-SVIN active
356
static const struct vlist_t vsvin_active[] = {
357
    {0, "Inactive"},
358
    {1, "Active"},
359
    {0, NULL},
360
};
361
362
// UBX-NAV-SVIN valid
363
// UBX-TIM-SVIN valid
364
static const struct vlist_t vsvin_valid[] = {
365
    {0, "Invalid"},
366
    {1, "Valid"},
367
    {0, NULL},
368
};
369
370
// UBX-NAV-SVINFO flags
371
static const struct flist_t fsvinfo_flags[] = {
372
    {1, 1, "svUsed"},
373
    {2, 2, "diffCorr"},
374
    {4, 4, "orbitAvail"},
375
    {8, 8, "orbitEph"},
376
    {0x10, 0x10, "unhealthy"},
377
    {0x20, 0x20, "orbitAlm"},
378
    {0x40, 0x40, "orbitAop"},
379
    {0x80, 0x80, "smoothed"},
380
    {0, 0, NULL},
381
};
382
383
// UBX-NAV-SVINFO globalFlags
384
static const struct vlist_t vglobalFlags[] = {
385
    {0, "Antaris 4"},
386
    {1, "u-blox 5"},
387
    {2, "u-blox 6"},
388
    {3, "u-blox 7"},
389
    {4, "u-blox 8"},
390
    {0, NULL},
391
};
392
393
// UBX-NAV-SAT, UBX-NAV-SVINFO qualtyiInd
394
static const struct vlist_t vquality[] = {
395
    {0, "None"},
396
    {1, "Searching"},
397
    {2, "Acquired"},
398
    {3, "Unusable"},
399
    {4, "Code locked"},
400
    {5, "Carrier locked"},
401
    {6, "Carrier locked"},
402
    {7, "Carrier locked"},
403
    {0, NULL},
404
};
405
406
// UBX-NAV-TIMEGPS valid
407
static const struct flist_t vtimegps_valid[] = {
408
    {1, 1, "towValid"},
409
    {2, 2, "weekValid"},
410
    {4, 4, "leapSValid"},
411
    {0, 0, NULL},
412
};
413
414
// UBX-NAV-TIMELS srcOfCurrLs
415
static const struct vlist_t vsrcOfCurrLs[] = {
416
    {0, "firmware"},
417
    {1, "GPS GLONASS difference"},
418
    {2, "GPS"},
419
    {3, "SBAS"},
420
    {4, "BeiDou"},
421
    {5, "Galileo"},
422
    {6, "Aided data"},
423
    {7, "Configured"},
424
    {0, NULL},
425
};
426
427
// UBX-NAV-TIMELS srcOfLsChange
428
static const struct vlist_t vsrcOfLsChange[] = {
429
    {0, "No Source"},
430
    {1, "Undefined"},
431
    {2, "GPS"},
432
    {3, "SBAS"},
433
    {4, "BeiDou"},
434
    {5, "Galileo"},
435
    {6, "GLONASS"},
436
    {0, NULL},
437
};
438
439
// UBX-NAV-TIMELS valid
440
static const struct flist_t vtimels_valid[] = {
441
    {1, 1, "validCurrLs"},
442
    {2, 2, "validTimeToLsEvent"},
443
    {0, 0, NULL},
444
};
445
446
// start ubx message configuration stuff
447
448
/* UBX-NAV-SOL deprecated in u-blox 6, gone in u-blox 9.
449
 * Use UBX-NAV-PVT after u-blox 7 (protver 15+)
450
 * u-blox 6 w/ GLONASS, protver 14 have NAV-PVT
451
 * UBX-NAV-SOL has same data from NAV-POSECEF and NAV-VELECEF.
452
 * Need NAV-SOL for fix type and fix flags.
453
 * skip NAV-POSLLH as we compute lat/lon/alt/geoid from ECEF.
454
 *
455
 * UBX-NAV-SVINFO deprecated in u-blox 8, gone in u-blox 9.
456
 * Use UBX-NAV-SAT after u-blox 7
457
 *
458
 * UBX-NAV-EOE makes a good cycle ender */
459
460
// nmea to turn off
461
static const unsigned char nmea_off[] = {
462
    0x00,          // msg id  = GGA
463
    0x01,          // msg id  = GLL
464
    0x02,          // msg id  = GSA
465
    0x03,          // msg id  = GSV
466
    0x04,          // msg id  = RMC
467
    0x05,          // msg id  = VTG
468
    0x07,          // msg id  = GST
469
    0x08,          // msg id  = ZDA
470
    0x09,          // msg id  = GBS
471
};
472
473
// UBX-NAV that ww want on, for all protver
474
static const unsigned char ubx_nav_on[] = {
475
    0x04,          // UBX-NAV-DOP
476
    0x20,          // UBX-NAV-TIMEGPS
477
    // UBX-NAV-CLOCK, nice cycle ender if no NAV-EOE (protVer 18)
478
    0x22,
479
};
480
481
// UBX-NAV for protver < 15, not present in protVer >= 27
482
static const unsigned char ubx_14_nav_on[] = {
483
    0x06,              // msg id = NAV-SOL
484
    0x30,              // msg id = NAV-SVINFO
485
};
486
487
// UBX for protver >= 15
488
static const unsigned char ubx_15_nav_on[] = {
489
    // Need NAV-POSECEF, NAV-VELECEF and NAV-PVT to replace NAV-SOL
490
    0x01,              // msg id = NAV-POSECEF
491
    0x07,              // msg id = NAV-PVT
492
    0x11,              // msg id = NAV-VELECEF
493
    0x35,              // msg id = NAV-SAT
494
    0x43,              // msg id = NAV-SIG
495
};
496
497
// end ubx message configuration stuff
498
499
static gps_mask_t ubx_msg_inf(struct gps_device_t *session, unsigned char *buf,
500
                              size_t data_len);
501
static gps_mask_t ubx_msg_log_batch(struct gps_device_t *session,
502
                                    unsigned char *buf, size_t data_len);
503
static gps_mask_t ubx_msg_log_info(struct gps_device_t *session,
504
                                   unsigned char *buf, size_t data_len);
505
static gps_mask_t ubx_msg_log_retrievepos(struct gps_device_t *session,
506
                                          unsigned char *buf, size_t data_len);
507
static gps_mask_t ubx_msg_log_retrieveposextra(struct gps_device_t *session,
508
                                               unsigned char *buf,
509
                                               size_t data_len);
510
static gps_mask_t ubx_msg_log_retrievestring(struct gps_device_t *session,
511
                                             unsigned char *buf,
512
                                             size_t data_len);
513
static gps_mask_t ubx_msg_nav_dop(struct gps_device_t *session,
514
                                  unsigned char *buf, size_t data_len);
515
static gps_mask_t ubx_msg_nav_eoe(struct gps_device_t *session,
516
                                  unsigned char *buf, size_t data_len);
517
static gps_mask_t ubx_msg_nav_posecef(struct gps_device_t *session,
518
                                      unsigned char *buf, size_t data_len);
519
static gps_mask_t ubx_msg_nav_pvt(struct gps_device_t *session,
520
                                  unsigned char *buf, size_t data_len);
521
static gps_mask_t ubx_msg_nav_sat(struct gps_device_t *session,
522
                                  unsigned char *buf, size_t data_len);
523
static gps_mask_t ubx_msg_nav_sbas(struct gps_device_t *session,
524
                                   unsigned char *buf, size_t data_len);
525
static gps_mask_t ubx_msg_nav_sol(struct gps_device_t *session,
526
                                  unsigned char *buf, size_t data_len);
527
static gps_mask_t ubx_msg_nav_svinfo(struct gps_device_t *session,
528
                                     unsigned char *buf, size_t data_len);
529
static gps_mask_t ubx_msg_nav_timegps(struct gps_device_t *session,
530
                                      unsigned char *buf, size_t data_len);
531
static gps_mask_t ubx_msg_sec_uniqid(struct gps_device_t *session,
532
                                  unsigned char *buf, size_t data_len);
533
static gps_mask_t ubx_msg_nav_velecef(struct gps_device_t *session,
534
                                      unsigned char *buf, size_t data_len);
535
static gps_mask_t ubx_msg_tim_tp(struct gps_device_t *session,
536
                                 unsigned char *buf, size_t data_len);
537
static void ubx_mode(struct gps_device_t *session, int mode);
538
539
typedef struct {
540
    const char *fw_string;
541
    const float protver;
542
} fw_protver_map_entry_t;
543
544
/* based on u-blox document no. GPS.G7-SW-12001-B1 (15 June 2018)
545
 * capture decimal parts of protVer info even when session->protver currently
546
 * is integer (which _might_ change in the future, so avoid having to revisit
547
 * the info at that time).
548
 * This list is substantially incomplete and over specific. */
549
static const fw_protver_map_entry_t fw_protver_map[] = {
550
    {"2.10", 8.10},           // antaris 4, version 8 is a guess
551
    {"2.11", 8.11},           // antaris 4, version 8 is a guess
552
    {"3.04", 9.00},           // antaris 4, version 9 is a guess
553
    {"4.00", 10.00},          // antaris 4, and u-blox 5
554
    {"4.01", 10.01},          // antaris 4, and u-blox 5
555
    {"5.00", 11.00},          // u-blox 5 and antaris 4
556
    {"6.00", 12.00},          // u-blox 5 and 6
557
    {"6.02", 12.02},          // u-blox 5 and 6
558
    {"6.02", 12.03},          // u-blox 5 and 6
559
    {"7.01", 13.01},          // u-blox 7
560
    {"7.03", 13.03},          // u-blox 6 and 7
561
    {"1.00", 14.00},          // u-blox 6 w/ GLONASS, and 7
562
    // protVer >14 should carry explicit protVer in MON-VER extension
563
    {NULL, 0.0},
564
};
565
566
// Fix GPS week since we know UTC time
567
static void fix_week(struct gps_device_t * session)
568
770
{
569
    // we hope leap seconds is correct...
570
770
    time_t secs_since_gps_epoch =
571
770
        (session->newdata.time.tv_sec + session->context->leap_seconds -
572
770
         GPS_EPOCH);
573
574
    // compute GPS week
575
770
    session->context->gps_week = secs_since_gps_epoch / 604800;
576
770
    session->context->rollovers = (int)(secs_since_gps_epoch / GPS_ROLLOVER);
577
770
}
578
579
/*
580
 * Model  Fw          Protver
581
 * M8     2,01        15.00
582
 * M9     HPG 1.13    27.12
583
 * M10    SPG 5.00    34.00
584
 * F20P   HPG 2.02    50.10
585
 */
586
587
/* send a ubx message.
588
 * calculate checksums, etc.
589
 */
590
bool ubx_write(struct gps_device_t * session,
591
               unsigned int msg_class, unsigned int msg_id,
592
               const unsigned char *msg, size_t data_len)
593
11.4k
{
594
11.4k
    unsigned char CK_A, CK_B;
595
11.4k
    ssize_t count;
596
11.4k
    size_t i;
597
11.4k
    bool ok;
598
599
    // do not write if -b (readonly) option set
600
    // "passive" handled earlier
601
11.4k
    if (session->context->readonly) {
602
0
        return true;
603
0
    }
604
605
11.4k
    session->msgbuf[0] = 0xb5;
606
11.4k
    session->msgbuf[1] = 0x62;
607
608
11.4k
    CK_A = CK_B = 0;
609
11.4k
    session->msgbuf[2] = msg_class;
610
11.4k
    session->msgbuf[3] = msg_id;
611
11.4k
    session->msgbuf[4] = data_len & 0xff;
612
11.4k
    session->msgbuf[5] = (data_len >> 8) & 0xff;
613
614
11.4k
    if ((sizeof(session->msgbuf) - 8) <= data_len) {
615
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
616
0
                 "=> GPS: UBX class: %02x, id: %02x, len: %zd TOO LONG!\n",
617
0
                 msg_class, msg_id, data_len);
618
0
    }
619
11.4k
    if (NULL != msg &&
620
7.51k
        0 < data_len) {
621
7.51k
        (void)memcpy(&session->msgbuf[6], msg, data_len);
622
7.51k
    }
623
624
    // calculate CRC
625
84.2k
    for (i = 2; i < (6 + data_len); i++) {
626
72.7k
        CK_A += session->msgbuf[i];
627
72.7k
        CK_B += CK_A;
628
72.7k
    }
629
630
11.4k
    session->msgbuf[6 + data_len] = CK_A;
631
11.4k
    session->msgbuf[7 + data_len] = CK_B;
632
11.4k
    session->msgbuflen = data_len + 8;
633
634
11.4k
    GPSD_LOG(LOG_PROG, &session->context->errout,
635
11.4k
             "=> GPS: UBX class: %02x, id: %02x, len: %zd, crc: %02x%02x\n",
636
11.4k
             msg_class, msg_id, data_len,
637
11.4k
             CK_A, CK_B);
638
11.4k
    count = gpsd_write(session, session->msgbuf, session->msgbuflen);
639
11.4k
    ok = (count == (ssize_t) session->msgbuflen);
640
11.4k
    return (ok);
641
11.4k
}
642
643
/* aPower2ant_power()
644
 * convert UBX antenna power flag to gpsd ant_power flag.
645
 * Used by UBX-MON-HW, and UBX-MON-RF.
646
 *
647
 */
648
static int aPower2ant_power(unsigned aPower)
649
469
{
650
469
    int ant_power;
651
652
469
    switch(aPower) {
653
69
    case 0:
654
        // Power off
655
69
        ant_power = ANT_PWR_OFF;
656
69
        break;
657
116
    case 1:
658
        // Power on
659
116
        ant_power = ANT_PWR_ON;
660
116
        break;
661
90
    case 2:
662
        // Power state unknown
663
90
        FALLTHROUGH
664
284
    default:
665
        // Unknown values
666
284
        ant_power = ANT_PWR_UNK;
667
284
       break;
668
469
    }
669
469
    return ant_power;
670
469
}
671
672
/* antStat2ant_stat()
673
 * convert UBX antenna status flag to gpsd ant_stat flag.
674
 * Used by UBX-MON-HW, and UBX-MON-RF.
675
 *
676
 */
677
static int antStat2ant_status(unsigned antStat)
678
469
{
679
469
    int ant_stat;
680
681
469
    switch (antStat) {
682
95
    case 2:
683
95
        ant_stat = ANT_OK;
684
95
        break;
685
78
    case 3:
686
78
        ant_stat = ANT_SHORT;
687
78
        break;
688
21
    case 4:
689
21
        ant_stat = ANT_OPEN;
690
21
        break;
691
67
    case 0:
692
        // Init
693
67
        FALLTHROUGH
694
169
    case 1:
695
        // Unknown
696
169
        FALLTHROUGH
697
275
    default:
698
        // Dunno...
699
275
        ant_stat = ANT_UNK;
700
275
        break;
701
469
    }
702
469
    return ant_stat;
703
469
}
704
705
/* Convert a ubx PRN (single svid) to an NMEA 4.0 (extended)
706
 * PRN and ubx gnssid, svid
707
 *
708
 * This does NOT match NMEA 4.10 and 4.11 where all PRN are 1-99,
709
 * except IMES, QZSS, and some SBAS.
710
 *
711
 * return 0 on fail
712
 */
713
static short ubx_to_prn(int ubx_PRN, gnssid_t *gnssId,
714
                        unsigned char *svId)
715
35.9k
{
716
35.9k
    *gnssId = 0;
717
35.9k
    *svId = 0;
718
719
    // IRNSS??
720
35.9k
    if (1 > ubx_PRN) {
721
        // skip 0 PRN
722
23.8k
        return 0;
723
23.8k
    } else if (32 >= ubx_PRN) {
724
        // GPS 1..32 -> 1..32
725
1.63k
        *gnssId = GNSSID_GPS;
726
1.63k
        *svId = ubx_PRN;
727
10.4k
    } else if (64 >= ubx_PRN) {
728
        /* BeiDou, 159..163,33..64 -> 1..5,6..37
729
         * Wikipedia, March 2025, says BDS PRNs go up to 62
730
         * Where/how do they map??
731
         * https://en.wikipedia.org/wiki/List_of_BeiDou_satellites */
732
1.78k
        *gnssId = GNSSID_BD;
733
1.78k
        *svId = ubx_PRN - 27;
734
8.69k
    } else if (96 >= ubx_PRN) {
735
        // GLONASS 65..96 -> 1..32
736
1.16k
        *gnssId = GNSSID_GLO;
737
1.16k
        *svId = ubx_PRN - 64;
738
7.52k
    } else if (120 > ubx_PRN) {
739
        // Huh?
740
674
        return 0;
741
6.85k
    } else if (158 >= ubx_PRN) {
742
        // SBAS 120..158 -> 120..158
743
1.53k
        *gnssId = GNSSID_SBAS;
744
1.53k
        *svId = ubx_PRN;
745
5.32k
    } else if (163 >= ubx_PRN) {
746
        // BeiDou, 159..163 -> 1..5
747
569
        *gnssId = GNSSID_BD;
748
569
        *svId = ubx_PRN - 158;
749
4.75k
    } else if (173 > ubx_PRN) {
750
        // Huh?
751
459
        return GNSSID_GPS;
752
4.29k
    } else if (182 >= ubx_PRN) {
753
        // IMES 173..182 -> 1..5, in u-blox 8, bot u-blox 9
754
957
        *gnssId = GNSSID_IMES;
755
957
        *svId = ubx_PRN - 172;
756
3.33k
    } else if (193 > ubx_PRN) {
757
        // Huh?
758
631
        return 0;
759
2.70k
    } else if (199 >= ubx_PRN) {
760
        // QZSS 193..197 -> 1..5
761
        // ZED-F9T also see 198 and 199
762
605
        *gnssId = GNSSID_QZSS;
763
605
        *svId = ubx_PRN - 192;
764
2.10k
    } else if (211 > ubx_PRN) {
765
        // Huh?
766
496
        return 0;
767
1.60k
    } else if (246 >= ubx_PRN) {
768
        // Galileo 211..246 -> 1..36
769
588
        *gnssId = GNSSID_GAL;
770
588
        *svId = ubx_PRN - 210;
771
1.01k
    } else {
772
        // greater than 246, GLONASS (255), unused, or other unknown
773
1.01k
        return 0;
774
1.01k
    }
775
8.83k
    return ubx2_to_prn(*gnssId, *svId);
776
35.9k
}
777
778
// UBX-ACK-ACK, UBX-ACK-NAK
779
static gps_mask_t ubx_msg_ack(struct gps_device_t *session,
780
                              unsigned char *buf, size_t data_len)
781
67
{
782
67
    unsigned msgid = getbes16(buf, 2);
783
784
67
    if (2 > data_len) {
785
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
786
2
                 "UBX: %s-: runt payload len %zd",
787
2
                 val2str(msgid, vack_ids), data_len);
788
2
        return 0;
789
2
    }
790
65
    GPSD_LOG(LOG_PROG, &session->context->errout,
791
65
             "UBX: %s: class: %02x, id: %02x\n",
792
65
             val2str(msgid, vack_ids),
793
65
             buf[UBX_PREFIX_LEN],
794
65
             buf[UBX_PREFIX_LEN + 1]);
795
65
    return 0;
796
67
}
797
798
// UBX-CFG-DOSC
799
static gps_mask_t ubx_msg_cfg_dosc(struct gps_device_t *session,
800
                                   unsigned char *buf, size_t data_len)
801
5
{
802
5
    unsigned version, numOsc, reserved1;
803
804
5
    if (4 > data_len) {
805
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
806
1
                 "UBX: CFG-DOSC, runt payload len %zd", data_len);
807
1
        return 0;
808
1
    }
809
4
    version = getub(buf, 0);
810
811
4
    if (0 != version) {
812
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
813
1
                 "UBX: CFG-DOSC, unknown version %u\n", version);
814
1
        return 0;
815
1
    }
816
817
3
    numOsc = getub(buf, 1);
818
3
    if (2 < numOsc) {
819
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
820
1
                 "UBX: CFG-DOSC, invalid numOsc %u\n", numOsc);
821
1
        return 0;
822
1
    }
823
2
    reserved1 = getleu16(buf, 2);
824
825
2
    GPSD_LOG(LOG_PROG, &session->context->errout,
826
2
             "UBX: CFG-DOSC: version %u numOsc %u reserved1 x%x \n",
827
2
             version, numOsc, reserved1);
828
829
2
    return 0;
830
3
}
831
832
// UBX-CFG-ESRC
833
static gps_mask_t ubx_msg_cfg_esrc(struct gps_device_t *session,
834
                                   unsigned char *buf, size_t data_len)
835
11
{
836
11
    unsigned version, numSources, reserved1;
837
838
11
    if (4 > data_len) {
839
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
840
1
                 "UBX: CFG-ESRC, runt payload len %zd", data_len);
841
1
        return 0;
842
1
    }
843
10
    version = getub(buf, 0);
844
845
10
    if (0 != version) {
846
7
        GPSD_LOG(LOG_WARN, &session->context->errout,
847
7
                 "UBX: CFG-DOSC, unknown version %u\n", version);
848
7
        return 0;
849
7
    }
850
851
3
    numSources = getub(buf, 1);
852
3
    if (2 < numSources) {
853
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
854
2
                 "UBX: CFG-ESRC, invalid numSources %u\n", numSources);
855
2
        return 0;
856
2
    }
857
1
    reserved1 = getleu16(buf, 2);
858
859
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
860
1
             "UBX: CFG-ESRC: version %u numSources %u reserved1 x%x \n",
861
1
             version, numSources, reserved1);
862
863
1
    return 0;
864
3
}
865
866
// UBX-CFG-RATE
867
// Deprecated in u-blox 10
868
static gps_mask_t ubx_msg_cfg_rate(struct gps_device_t *session,
869
                                   unsigned char *buf, size_t data_len)
870
7
{
871
7
    uint16_t measRate, navRate, timeRef;
872
873
7
    if (6 > data_len) {
874
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
875
2
                 "UBX: CFG-RATE, runt payload len %zd", data_len);
876
2
        return 0;
877
2
    }
878
879
5
    measRate = getleu16(buf, 0);  // Measurement rate (ms)
880
5
    navRate = getleu16(buf, 2);   // Navigation rate (cycles)
881
5
    timeRef = getleu16(buf, 4);   // Time system, e.g. UTC, GPS, ...
882
883
5
    GPSD_LOG(LOG_PROG, &session->context->errout,
884
5
             "UBX: CFG-RATE: measRate %ums, navRate %u cycle(s), timeRef %u\n",
885
5
             (unsigned)measRate, (unsigned)navRate,
886
5
             (unsigned)timeRef);
887
888
    // Update our notion of what the device's measurement rate is
889
5
    MSTOTS(&session->gpsdata.dev.cycle, measRate);
890
891
5
    return 0;
892
7
}
893
894
/* UBX-CFG-VALGET
895
 * Present in protVer 24 and up
896
 */
897
static gps_mask_t ubx_msg_cfg_valget(struct gps_device_t *session,
898
                                   unsigned char *buf, size_t data_len)
899
5
{
900
5
    unsigned version, layer, position;
901
902
5
    if (4 > data_len) {
903
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
904
1
                 "UBX: CFG-VALGET, runt payload len %zd", data_len);
905
1
        return 0;
906
1
    }
907
908
4
    version = getub(buf, 0);        // version
909
910
4
    if (1 != version) {
911
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
912
3
                 "UBX: CFG-VALGET, unknown version %u\n", version);
913
3
        return 0;
914
3
    }
915
916
1
    layer = getub(buf, 1);           // layer
917
1
    position = getleu16(buf, 2);     // position
918
919
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
920
1
             "UBX: CFG-VALGET: version %u layer %u position %u\n",
921
1
             version, layer, position);
922
923
    // FIXME: get the key/value pairs.
924
925
1
    return 0;
926
4
}
927
928
/* UBX-ESF-ALG
929
 *
930
 * UBX-ESF-ALG, and UBX-ESF-INS are synchronous to the GNSS epoch.
931
 * They need to be combined and reported together with the rest of
932
 * the epoch.
933
 */
934
static gps_mask_t ubx_msg_esf_alg(struct gps_device_t *session,
935
                                  unsigned char *buf, size_t data_len)
936
6
{
937
6
    unsigned version, flags, error, reserved1;
938
6
    unsigned long yaw;
939
6
    int pitch, roll;
940
6
    static gps_mask_t mask = 0;
941
942
6
    if (16 > data_len) {
943
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
944
1
                 "UBX: ESF-ALG: runt payload len %zd", data_len);
945
1
        return mask;
946
1
    }
947
948
    // UBX-ESF-ALG is aligned with the GNSS epoch.
949
5
    session->driver.ubx.iTOW = getleu32(buf, 0);
950
951
5
    version = getub(buf, 4);
952
5
    flags = getub(buf, 5);
953
5
    error = getub(buf, 6);
954
5
    reserved1 = getub(buf, 7);
955
5
    yaw = getleu32(buf, 8);
956
5
    pitch = getles16(buf, 12);
957
5
    roll = getles16(buf, 14);
958
959
5
    if (0 == (2 & error)) {
960
        // no yawAlgError
961
4
        session->gpsdata.attitude.yaw = 0.01 * yaw;
962
4
        mask |= ATTITUDE_SET;
963
4
    }
964
5
    if (0 == (5 & error)) {
965
        // no tiltAlgError or angleError
966
3
        session->gpsdata.attitude.roll = 0.01 * roll;
967
3
        session->gpsdata.attitude.pitch = 0.01 * pitch;
968
3
        mask |= ATTITUDE_SET;
969
3
    }
970
971
5
    if (0 != mask) {
972
5
        timespec_t ts_tow;
973
        // got good data, set the measurement time
974
5
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
975
5
        session->gpsdata.attitude.mtime =
976
5
            gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
977
5
    }
978
5
    GPSD_LOG(LOG_PROG, &session->context->errout,
979
5
             "UBX: ESF-ALG: iTOW %lld version %u flags x%x error x%x"
980
5
             " reserved1 x%x yaw %ld pitch %u roll %u\n",
981
5
            (long long)session->driver.ubx.iTOW, version, flags, error,
982
5
            reserved1, yaw, pitch, roll);
983
984
5
    return mask;
985
6
}
986
987
/* UBX-ESF-INS
988
 *
989
 * protVer 19 and up.  ADR and UDR only
990
 *
991
 * UBX-ESF-ALG, and UBX-ESF-INS are synchronous to the GNSS epoch.
992
 * They need to be combined and reported together with the rest of
993
 * the epoch.
994
 */
995
static gps_mask_t ubx_msg_esf_ins(struct gps_device_t *session,
996
                                  unsigned char *buf, size_t data_len)
997
9
{
998
9
    unsigned long long bitfield0, reserved1;
999
9
    long xAngRate, yAngRate, zAngRate;
1000
9
    long xAccel, yAccel, zAccel;
1001
9
    static gps_mask_t mask = 0;
1002
1003
9
    if (16 > data_len) {
1004
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
1005
3
                 "UBX: ESF-INS: runt payload len %zd", data_len);
1006
3
        return mask;
1007
3
    }
1008
1009
6
    bitfield0 = getleu32(buf, 0);
1010
6
    reserved1 = getleu32(buf, 4);
1011
    // UBX-ESF-INS is aligned with the GNSS epoch.
1012
6
    session->driver.ubx.iTOW = getleu32(buf, 8);
1013
6
    xAngRate = getles32(buf, 12);
1014
6
    yAngRate = getles32(buf, 16);
1015
6
    zAngRate = getles32(buf, 20);
1016
6
    xAccel = getles32(buf, 24);
1017
6
    yAccel = getles32(buf, 28);
1018
6
    zAccel = getles32(buf, 32);
1019
1020
6
    if (0x100 == (0x100 & bitfield0)) {
1021
        // xAngRateValid
1022
3
        session->gpsdata.attitude.gyro_x = 0.001 * xAngRate;  // deg/s
1023
3
        mask |= ATTITUDE_SET;
1024
3
    }
1025
6
    if (0x200 == (0x200 & bitfield0)) {
1026
        // yAngRateValid
1027
1
        session->gpsdata.attitude.gyro_y = 0.001 * yAngRate;  // deg/s
1028
1
        mask |= ATTITUDE_SET;
1029
1
    }
1030
6
    if (0x400 == (0x400 & bitfield0)) {
1031
        // zAngRateValid
1032
2
        session->gpsdata.attitude.gyro_z = 0.001 * zAngRate;  // deg/s
1033
2
        mask |= ATTITUDE_SET;
1034
2
    }
1035
6
    if (0x800 == (0x800 & bitfield0)) {
1036
        // xAccelValid
1037
1
        session->gpsdata.attitude.acc_x = 0.01 * xAccel;  // m/s^2
1038
1
        mask |= ATTITUDE_SET;
1039
1
    }
1040
6
    if (0x1000 == (0x1000 & bitfield0)) {
1041
        // yAccelValid
1042
3
        session->gpsdata.attitude.acc_y = 0.01 * yAccel;  // m/s^2
1043
3
        mask |= ATTITUDE_SET;
1044
3
    }
1045
6
    if (0x2000 == (0x2000 & bitfield0)) {
1046
        // zAccelValid
1047
2
        session->gpsdata.attitude.acc_z = 0.01 * zAccel;  // m/s^2
1048
2
        mask |= ATTITUDE_SET;
1049
2
    }
1050
1051
6
    if (0 != mask) {
1052
6
        timespec_t ts_tow;
1053
        // got good data, set the measurement time
1054
6
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1055
6
        session->gpsdata.attitude.mtime =
1056
6
            gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
1057
6
    }
1058
6
    GPSD_LOG(LOG_PROG, &session->context->errout,
1059
6
             "UBX: ESF-INS: bitfield0 %llu, reserved1 %llu iTOW %lld"
1060
6
             " xAngRate %ld yAngRate %ld zAngRate %ld"
1061
6
             " xAccel %ld yAccel %ld zAccel %ld\n",
1062
6
            bitfield0, reserved1,
1063
6
            (long long)session->driver.ubx.iTOW,
1064
6
            xAngRate, yAngRate, zAngRate, xAccel, yAccel, zAccel);
1065
1066
6
    return mask;
1067
9
}
1068
1069
/* UBX-ESF-MEAS
1070
 *
1071
 * protVer 15 and up.  ADR only
1072
 * protVer 19 and up.  ADR and UDR only
1073
 *
1074
 * asynchronous to the GNSS epoch, and at a higher rate.
1075
 * Needs to be reported immediately.
1076
 *
1077
 */
1078
static gps_mask_t ubx_msg_esf_meas(struct gps_device_t *session,
1079
                                   unsigned char *buf, size_t data_len)
1080
98
{
1081
98
    unsigned flags, id, numMeas, expected_len;
1082
98
    gps_mask_t mask = 0;
1083
98
    unsigned i;
1084
    // where to store the IMU data.
1085
98
    struct attitude_t *datap = &session->gpsdata.imu[0];
1086
1087
98
    if (8 > data_len) {
1088
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
1089
2
                 "UBX: ESF-MEAS: runt payload len %zd", data_len);
1090
2
        return mask;
1091
2
    }
1092
    // do not accumulate IMU data
1093
96
    gps_clear_att(datap);
1094
96
    (void)strlcpy(datap->msg, "UBX-ESF-MEAS", sizeof(datap->msg));
1095
1096
96
    datap->timeTag = getleu32(buf, 0);
1097
96
    flags = getleu16(buf, 4);
1098
96
    numMeas = (flags >> 11) & 0x01f;
1099
96
    id = getleu16(buf, 6);
1100
96
    expected_len = 8 + (4 * numMeas);
1101
96
    if (0x08 & flags) {
1102
19
        expected_len += 4;
1103
19
    }
1104
96
    if (expected_len != data_len) {
1105
9
        GPSD_LOG(LOG_WARN, &session->context->errout,
1106
9
                 "UBX: ESF-MEAS: bad length.  Got %zd, expected %u",
1107
9
                 data_len, expected_len);
1108
9
        return 0;
1109
9
    }
1110
1111
87
    GPSD_LOG(LOG_PROG, &session->context->errout,
1112
87
             "UBX: ESF-MEAS: timeTag %lu flags x%x (numMeas %u) id %u\n",
1113
87
             datap->timeTag, flags, numMeas, id);
1114
1115
985
    for (i = 0; i < numMeas; i++) {
1116
898
        unsigned long data, dataField;
1117
898
        long dataF;
1118
898
        unsigned char dataType;
1119
1120
898
        data = getleu32(buf, 8 + (i * 4));
1121
898
        dataType = (unsigned char)(data >> 24) & 0x3f;
1122
898
        dataField = data & BITMASK(24);
1123
898
        switch (dataType) {
1124
108
        case 5:            // gyro z angular rate, deg/s^2
1125
108
            dataF = UINT2INT(dataField, 24);
1126
108
            datap->gyro_z = dataF / 4096.0;
1127
108
            mask |= IMU_SET;
1128
108
            break;
1129
90
        case 12:           // gyro temp, deg C
1130
90
            dataF = UINT2INT(dataField, 24);
1131
90
            datap->gyro_temp = dataF / 100.0;
1132
90
            mask |= IMU_SET;
1133
90
            break;
1134
100
        case 13:           // gyro y angular rate, deg/s^2
1135
100
            dataF = UINT2INT(dataField, 24);
1136
100
            datap->gyro_y = dataF / 4096.0;
1137
100
            mask |= IMU_SET;
1138
100
            break;
1139
103
        case 14:           // gyro x angular rate, deg/s^2
1140
103
            dataF = UINT2INT(dataField, 24);
1141
103
            datap->gyro_x = dataF / 4096.0;
1142
103
            mask |= IMU_SET;
1143
103
            break;
1144
104
        case 16:            // accel x, m/s^2
1145
104
            dataF = UINT2INT(dataField, 24);
1146
104
            datap->acc_x = dataF / 1024.0;
1147
104
            mask |= IMU_SET;
1148
104
            break;
1149
87
        case 17:           // accel y, m/s^2
1150
87
            dataF = UINT2INT(dataField, 24);
1151
87
            datap->acc_y = dataF / 1024.0;
1152
87
            mask |= IMU_SET;
1153
87
            break;
1154
87
        case 18:           // accel z, m/s^2
1155
87
            dataF = UINT2INT(dataField, 24);
1156
87
            datap->acc_z = dataF / 1024.0;
1157
87
            mask |= IMU_SET;
1158
87
            break;
1159
        // case 6:            // front-left wheel ticks
1160
        // case 7:            // front-right wheel ticks
1161
        // case 8:            // rear-left wheel ticks
1162
        // case 9:            // rear-right wheel ticks
1163
        // case 10:           // speed tick
1164
        // case 11:           // speed, m/s
1165
219
        default:
1166
            // ignore all else
1167
219
            dataF = dataField;
1168
219
            break;
1169
898
        }
1170
1171
898
        GPSD_LOG(LOG_PROG + 1, &session->context->errout,
1172
898
                 "UBX: ESF-MEAS: dataType %2u dataField %9ld\n",
1173
898
                 dataType, dataF);
1174
898
    }
1175
1176
87
    return mask;
1177
87
}
1178
1179
/* UBX-ESF-RAW
1180
 *
1181
 * protVer 15 and up.  ADR only
1182
 * protVer 19 and up.  ADR and UDR only
1183
 *
1184
 * asynchronous to the GNSS epoch, and a a higher rate.
1185
 * Needs to be reported immediately.
1186
 *
1187
 */
1188
static gps_mask_t ubx_msg_esf_raw(struct gps_device_t *session,
1189
                                  unsigned char *buf, size_t data_len)
1190
189
{
1191
189
    unsigned long reserved1, last_sTtag = 0;
1192
189
    unsigned i;
1193
189
    uint16_t blocks;
1194
189
    gps_mask_t mask = 0;
1195
189
    struct attitude_t *datap = NULL;
1196
189
    int max_imu, cur_imu = -1;
1197
189
    max_imu = sizeof(session->gpsdata.imu) / sizeof(struct attitude_t);
1198
1199
189
    if (4 > data_len) {
1200
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
1201
1
                 "UBX: ESF-RAW:runt payload len %zd", data_len);
1202
1
        return mask;
1203
1
    }
1204
1205
188
    reserved1 = getleu32(buf, 0);  // reserved1
1206
188
    if (0 != ((data_len - 4) % 8)) {
1207
23
        GPSD_LOG(LOG_WARN, &session->context->errout,
1208
23
                 "UBX: ESF-RAW: weird payload len %zd", data_len);
1209
23
        return mask;
1210
23
    }
1211
165
    blocks = (data_len - 4) / 8;
1212
1213
165
    GPSD_LOG(LOG_PROG, &session->context->errout,
1214
165
             "UBX: ESF-RAW: reserved1 x%lx, blocks %u\n",
1215
165
             reserved1, blocks);
1216
1217
    // loop over all blocks, use the next imu[] when time changes.
1218
3.37k
    for (i = 0; i < blocks; i++) {
1219
3.23k
        unsigned long data, dataField, sTtag;
1220
3.23k
        long dataF;
1221
3.23k
        unsigned char dataType;
1222
1223
3.23k
        sTtag = getleu32(buf, 8 + (i * 8));
1224
3.23k
        if ((-1 == cur_imu) ||
1225
3.06k
            (last_sTtag != sTtag)) {
1226
728
            cur_imu++;
1227
728
            if (max_imu <= cur_imu) {
1228
20
                GPSD_LOG(LOG_WARN, &session->context->errout,
1229
20
                         "UBX: ESF-RAW: too many imu max %d block %u\n",
1230
20
                         max_imu, i);
1231
20
                break;
1232
20
            }
1233
708
            last_sTtag = sTtag;
1234
708
            datap = &session->gpsdata.imu[cur_imu];
1235
            // do not accumulate IMU data
1236
708
            gps_clear_att(datap);
1237
708
            (void)strlcpy(datap->msg, "UBX-ESF-RAW", sizeof(datap->msg));
1238
708
        }
1239
3.21k
        if (NULL == datap) {
1240
            // paranoia
1241
0
            continue;
1242
0
        }
1243
1244
3.21k
        data = getleu32(buf, 4 + (i * 8));
1245
3.21k
        dataType = (unsigned char)(data >> 24) & 0x3f;
1246
3.21k
        dataField = data & BITMASK(24);
1247
3.21k
        datap->timeTag = sTtag;
1248
3.21k
        switch (dataType) {
1249
281
        case 5:            // gyro z angular rate, deg/s^2
1250
281
            dataF = UINT2INT(dataField, 24);
1251
281
            datap->gyro_z = dataF / 4096.0;
1252
281
            mask |= IMU_SET;
1253
281
            break;
1254
433
        case 12:           // gyro temp, deg C
1255
433
            dataF = UINT2INT(dataField, 24);
1256
433
            datap->gyro_temp = dataF / 100.0;
1257
433
            mask |= IMU_SET;
1258
433
            break;
1259
422
        case 13:           // gyro y angular rate, deg/s^2
1260
422
            dataF = UINT2INT(dataField, 24);
1261
422
            datap->gyro_y = dataF / 4096.0;
1262
422
            mask |= IMU_SET;
1263
422
            break;
1264
427
        case 14:           // gyro x angular rate, deg/s^2
1265
427
            dataF = UINT2INT(dataField, 24);
1266
427
            datap->gyro_x = dataF / 4096.0;
1267
427
            mask |= IMU_SET;
1268
427
            break;
1269
428
        case 16:            // accel x, m/s^2
1270
428
            dataF = UINT2INT(dataField, 24);
1271
428
            datap->acc_x = dataF / 1024.0;
1272
428
            mask |= IMU_SET;
1273
428
            break;
1274
447
        case 17:           // accel y, m/s^2
1275
447
            dataF = UINT2INT(dataField, 24);
1276
447
            datap->acc_y = dataF / 1024.0;
1277
447
            mask |= IMU_SET;
1278
447
            break;
1279
315
        case 18:           // accel z, m/s^2
1280
315
            dataF = UINT2INT(dataField, 24);
1281
315
            datap->acc_z = dataF / 1024.0;
1282
315
            mask |= IMU_SET;
1283
315
            break;
1284
        // case 6:            // front-left wheel ticks
1285
        // case 7:            // front-right wheel ticks
1286
        // case 8:            // rear-left wheel ticks
1287
        // case 9:            // rear-right wheel ticks
1288
        // case 10:           // speed tick
1289
        // case 11:           // speed, m/s
1290
458
        default:
1291
            // ignore all else
1292
458
            dataF = dataField;
1293
458
            break;
1294
3.21k
        }
1295
1296
3.21k
        GPSD_LOG(LOG_PROG + 1, &session->context->errout,
1297
3.21k
                 "UBX: ESF-RAW: dataType %2u dataField %9ld sTtag %lu\n",
1298
3.21k
                 dataType, dataF, datap->timeTag);
1299
3.21k
    }
1300
165
    return mask;
1301
165
}
1302
1303
// UBX-ESF-STATUS
1304
static gps_mask_t ubx_msg_esf_status(struct gps_device_t *session,
1305
                                     unsigned char *buf, size_t data_len)
1306
151
{
1307
151
    unsigned version, fusionMode, numSens, expected_len;
1308
151
    static gps_mask_t mask = 0;
1309
1310
151
    if (16 > data_len) {
1311
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
1312
2
                 "UBX: ESF-STATUS:runt payload len %zd", data_len);
1313
2
        return mask;
1314
2
    }
1315
1316
149
    session->driver.ubx.iTOW = getleu32(buf, 0);
1317
149
    version = getub(buf, 4);
1318
149
    fusionMode = getub(buf, 12);
1319
149
    numSens = getub(buf, 15);
1320
149
    expected_len = 16 + (4 * numSens);
1321
1322
149
    if (expected_len != data_len) {
1323
76
        GPSD_LOG(LOG_WARN, &session->context->errout,
1324
76
                 "UBX: ESF-STATUS: bad length.  Expected %u got %zd",
1325
76
                 expected_len, data_len);
1326
76
        return mask;
1327
76
    }
1328
1329
73
    GPSD_LOG(LOG_PROG, &session->context->errout,
1330
73
             "UBX: ESF-STATUS: iTOW %lld version %u fusionMode %u numSens %u\n",
1331
73
            (long long)session->driver.ubx.iTOW, version, fusionMode, numSens);
1332
1333
73
    return mask;
1334
149
}
1335
1336
/**
1337
 * HNR Attitude solution
1338
 * UBX-HNR-ATT Class x28, ID 1
1339
 *
1340
 * Not before u-blox 8, protVer 19.2 and up.
1341
 * only on ADR, and UDR
1342
 */
1343
static gps_mask_t ubx_msg_hnr_att(struct gps_device_t *session,
1344
                                  unsigned char *buf, size_t data_len)
1345
13
{
1346
13
    uint8_t version;
1347
13
    int64_t iTOW;
1348
13
    timespec_t ts_tow;
1349
13
    gps_mask_t mask = 0;
1350
1351
13
    if (32 > data_len) {
1352
7
        GPSD_LOG(LOG_WARN, &session->context->errout,
1353
7
                 "UBX: HNR-ATT: runt payload len %zd", data_len);
1354
7
        return 0;
1355
7
    }
1356
1357
    // don't set session->driver.ubx.iTOW, HNR is off-cycle
1358
6
    iTOW = getleu32(buf, 0);
1359
6
    MSTOTS(&ts_tow, iTOW);
1360
6
    session->gpsdata.attitude.mtime =
1361
6
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
1362
1363
6
    version  = (unsigned int)getub(buf, 4);
1364
1365
6
    session->gpsdata.attitude.roll = 1e-5 * getles32(buf, 8);
1366
6
    session->gpsdata.attitude.pitch = 1e-5 * getles32(buf, 12);
1367
    // seems to be true heading
1368
6
    session->gpsdata.attitude.heading = 1e-5 * getles32(buf, 16);
1369
6
    mask |= ATTITUDE_SET;
1370
1371
6
    GPSD_LOG(LOG_PROG, &session->context->errout,
1372
6
         "UBX: HNR-ATT: iTOW %lld version %u roll %.5f pitch %.5f "
1373
6
         "heading %.5f\n",
1374
6
         (long long)iTOW,
1375
6
         version,
1376
6
         session->gpsdata.attitude.roll,
1377
6
         session->gpsdata.attitude.pitch,
1378
6
         session->gpsdata.attitude.heading);
1379
1380
6
    return mask;
1381
13
}
1382
1383
/**
1384
 * HNR Vehicle dynamics information
1385
 * UBX-HNR-INS Class x28, ID 2
1386
 *
1387
 * Not before u-blox 8, protVer 19.1 and up.
1388
 * only on ADR, and UDR
1389
 */
1390
static gps_mask_t ubx_msg_hnr_ins(struct gps_device_t *session,
1391
                                  unsigned char *buf, size_t data_len)
1392
8
{
1393
8
    uint8_t version;
1394
8
    uint32_t bitfield0;
1395
8
    gps_mask_t mask = 0;
1396
8
    int64_t iTOW;
1397
1398
8
    if (36 > data_len) {
1399
4
        GPSD_LOG(LOG_WARN, &session->context->errout,
1400
4
                 "UBX: HNR-INS: runt payload len %zd", data_len);
1401
4
        return 0;
1402
4
    }
1403
1404
4
    version  = (unsigned int)getub(buf, 0);
1405
1406
4
    bitfield0 = getleu32(buf, 0);
1407
    // don't set session->driver.ubx.iTOW, HNR is off-cycle
1408
4
    iTOW = getleu32(buf, 8);
1409
1410
4
    if (0x100 == (0x100 & bitfield0)) {
1411
        // xAngRateValid
1412
2
        session->gpsdata.attitude.gyro_x = 0.001 * getles32(buf, 12);  // deg/s
1413
2
        mask |= ATTITUDE_SET;
1414
2
    }
1415
4
    if (0x200 == (0x200 & bitfield0)) {
1416
        // yAngRateValid
1417
2
        session->gpsdata.attitude.gyro_y = 0.001 * getles32(buf, 16);  // deg/s
1418
2
        mask |= ATTITUDE_SET;
1419
2
    }
1420
4
    if (0x400 == (0x400 & bitfield0)) {
1421
        // zAngRateValid
1422
1
        session->gpsdata.attitude.gyro_z = 0.001 * getles32(buf, 20);  // deg/s
1423
1
        mask |= ATTITUDE_SET;
1424
1
    }
1425
4
    if (0x800 == (0x800 & bitfield0)) {
1426
        // xAccelValid
1427
1
        session->gpsdata.attitude.acc_x = 0.01 * getles32(buf, 24);  // m/s^2
1428
1
        mask |= ATTITUDE_SET;
1429
1
    }
1430
4
    if (0x1000 == (0x1000 & bitfield0)) {
1431
        // yAccelValid
1432
1
        session->gpsdata.attitude.acc_y = 0.01 * getles32(buf, 28);  // m/s^2
1433
1
        mask |= ATTITUDE_SET;
1434
1
    }
1435
4
    if (0x2000 == (0x2000 & bitfield0)) {
1436
        // zAccelValid
1437
1
        session->gpsdata.attitude.acc_z = 0.01 * getles32(buf, 32);  // m/s^2
1438
1
        mask |= ATTITUDE_SET;
1439
1
    }
1440
1441
4
    if (0 != mask) {
1442
2
        timespec_t ts_tow;
1443
        // got good data, set the measurement time
1444
2
        MSTOTS(&ts_tow, iTOW);
1445
2
        session->gpsdata.attitude.mtime =
1446
2
            gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
1447
2
    }
1448
1449
4
    GPSD_LOG(LOG_PROG, &session->context->errout,
1450
4
         "UBX: HNR-INS: iTOW %lld version %u bitfield0 x%x "
1451
4
         "gyro_x %.3f gyro_y %.3f gyro_z %.3f "
1452
4
         "acc_x %.3f acc_y %.3f acc_z %.3f\n",
1453
4
         (long long)iTOW,
1454
4
         version, bitfield0,
1455
4
         session->gpsdata.attitude.gyro_x,
1456
4
         session->gpsdata.attitude.gyro_y,
1457
4
         session->gpsdata.attitude.gyro_z,
1458
4
         session->gpsdata.attitude.acc_x,
1459
4
         session->gpsdata.attitude.acc_y,
1460
4
         session->gpsdata.attitude.acc_z);
1461
1462
4
    return mask;
1463
8
}
1464
1465
/**
1466
 * High rate output of PVT solution
1467
 * UBX-HNR-PVT Class x28, ID 2
1468
 *
1469
 * Present in:
1470
 *    protVer 19 and up
1471
 *    only on ADR, and UDR
1472
 */
1473
static gps_mask_t ubx_msg_hnr_pvt(struct gps_device_t *session,
1474
                                  unsigned char *buf, size_t data_len)
1475
184
{
1476
184
    char buf2[80];
1477
184
    char buf3[80];
1478
184
    char ts_buf[TIMESPEC_LEN];
1479
184
    gps_mask_t mask = 0;
1480
184
    int64_t iTOW;
1481
184
    int *mode = &session->newdata.mode;
1482
184
    int *status = &session->newdata.status;
1483
184
    struct tm unpacked_date = {0};
1484
184
    unsigned flags;
1485
184
    unsigned gpsFix;    // same as NAV-PVT typeFix
1486
184
    unsigned valid;
1487
1488
184
    if (72 > data_len) {
1489
7
        GPSD_LOG(LOG_WARN, &session->context->errout,
1490
7
                 "UBX: HNR-PVT: runt payload len %zd", data_len);
1491
7
        return 0;
1492
7
    }
1493
1494
    // don't set session->driver.ubx.iTOW, HNR is off-cycle
1495
177
    iTOW = getleu32(buf, 0);
1496
    // valid same as UBX-NAV-PVT valid
1497
177
    valid = (unsigned int)getub(buf, 11);
1498
    // gpsFix same as UBX-NAV-PVT fixType
1499
177
    gpsFix = (unsigned char)getub(buf, 16);
1500
    // flags NOT same as UBX-NAV-PVT flags
1501
177
    flags = (unsigned int)getub(buf, 17);
1502
1503
177
    switch (gpsFix) {
1504
2
    case UBX_MODE_TMONLY:
1505
        // 5 - Surveyed-in, so a precise 3D.
1506
2
        *mode = MODE_3D;
1507
2
        *status = STATUS_TIME;
1508
2
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
1509
2
        break;
1510
1511
2
    case UBX_MODE_3D:
1512
        // 3
1513
2
        *mode = MODE_3D;
1514
2
        *status = STATUS_GPS;
1515
2
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
1516
2
        break;
1517
1518
2
    case UBX_MODE_GPSDR:
1519
        // 4
1520
2
        *mode = MODE_3D;
1521
2
        *status = STATUS_GNSSDR;
1522
2
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
1523
2
        break;
1524
1525
1
    case UBX_MODE_2D:
1526
        // 2
1527
1
        *mode = MODE_2D;
1528
1
        *status = STATUS_GPS;
1529
1
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
1530
1
        break;
1531
1532
4
    case UBX_MODE_DR:           // consider this too as 2D
1533
        // 1
1534
        // should be 3D?
1535
4
        *mode = MODE_2D;
1536
4
        *status = STATUS_DR;
1537
4
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
1538
4
        break;
1539
1540
30
    case UBX_MODE_NOFIX:
1541
        // 0
1542
30
        FALLTHROUGH
1543
166
    default:
1544
        // huh?
1545
166
        *mode = MODE_NO_FIX;
1546
166
        *status = STATUS_UNK;
1547
166
        mask |= MODE_SET | STATUS_SET;
1548
166
        break;
1549
177
    }
1550
1551
177
    if (UBX_NAV_PVT_FLAG_DGPS == (flags & UBX_NAV_PVT_FLAG_DGPS)) {
1552
        // RTK flags not in u-blox 8
1553
89
        if (STATUS_TIME == *status) {
1554
            // leave it
1555
88
        } else if (UBX_NAV_PVT_FLAG_RTK_FIX ==
1556
88
                   (flags & UBX_NAV_PVT_FLAG_RTK_FIX)) {
1557
53
            *status = STATUS_RTK_FIX;
1558
53
        } else if (UBX_NAV_PVT_FLAG_RTK_FLT ==
1559
35
                   (flags & UBX_NAV_PVT_FLAG_RTK_FLT)) {
1560
13
            *status = STATUS_RTK_FLT;
1561
22
        } else {
1562
22
            *status = STATUS_DGPS;
1563
22
        }
1564
89
        mask |= STATUS_SET;
1565
89
    }
1566
1567
177
    if (UBX_NAV_PVT_VALID_DATE_TIME == (valid & UBX_NAV_PVT_VALID_DATE_TIME)) {
1568
171
        unpacked_date.tm_year = (uint16_t)getleu16(buf, 4) - 1900;
1569
171
        unpacked_date.tm_mon = (uint8_t)getub(buf, 6) - 1;
1570
171
        unpacked_date.tm_mday = (uint8_t)getub(buf, 7);
1571
171
        unpacked_date.tm_hour = (uint8_t)getub(buf, 8);
1572
171
        unpacked_date.tm_min = (uint8_t)getub(buf, 9);
1573
171
        unpacked_date.tm_sec = (uint8_t)getub(buf, 10);
1574
171
        unpacked_date.tm_isdst = 0;
1575
171
        unpacked_date.tm_wday = 0;
1576
171
        unpacked_date.tm_yday = 0;
1577
171
        session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
1578
        // field 9, nano, can be negative! So normalize
1579
171
        session->newdata.time.tv_nsec = getles32(buf, 12);
1580
171
        TS_NORM(&session->newdata.time);
1581
        // compute GPS week
1582
171
        fix_week(session);
1583
1584
171
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
1585
171
    }
1586
1587
177
    session->newdata.longitude = 1e-7 * getles32(buf, 20);
1588
177
    session->newdata.latitude = 1e-7 * getles32(buf, 24);
1589
    // altitude WGS84
1590
177
    session->newdata.altHAE = (double)1e-3 * getles32(buf, 28);
1591
    // altitude MSL, double to prevent promotion to (long double)
1592
177
    session->newdata.altMSL = (double)1e-3 * getles32(buf, 32);
1593
    // Let gpsd_error_model() deal with geoid_sep
1594
1595
    // gSpeed (2D)
1596
177
    session->newdata.speed = 1e-3 * (int32_t)getles32(buf, 36);
1597
    // offset 40,  Speed (3D) do what with it?
1598
    // u-blox calls this headMot (Heading of motion 2-D)
1599
177
    session->newdata.track = 1e-5 * (int32_t)getles32(buf, 44);
1600
    // offset 48, headVeh (Heading of Vehicle 2-D)
1601
177
    mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET;
1602
1603
    /* u-blox does not document the basis for the following "accuracy"
1604
     * estimates.  Maybe CEP(50), one sigma, two sigma, CEP(99), etc. */
1605
1606
    // Horizontal Accuracy estimate, in mm
1607
177
    session->newdata.eph = (double)(getles32(buf, 52) / 1000.0);
1608
    // Vertical Accuracy estimate, in mm
1609
177
    session->newdata.epv = (double)(getles32(buf, 56) / 1000.0);
1610
    // Speed Accuracy estimate, in mm/s
1611
177
    session->newdata.eps = (double)(getles32(buf, 60) / 1000.0);
1612
    // headAcc (Heading Accuracy)
1613
177
    session->newdata.epd = (double)getles32(buf, 64) * 1e-5;
1614
    // let gpsd_error_model() do the rest
1615
1616
    // 4 final bytes reserved
1617
1618
177
    mask |= HERR_SET | SPEEDERR_SET | VERR_SET;
1619
    // HNR-PVT interleaves with the normal cycle, so cycle end is a mess
1620
177
    mask |= REPORT_IS;
1621
1622
177
    GPSD_LOG(LOG_PROG, &session->context->errout,
1623
177
         "UBX: HNR-PVT: iTOW %lld flags %02x time %s lat %.2f lon %.2f "
1624
177
         "altHAE %.2f track %.2f speed %.2f climb %.2f mode %d status %d "
1625
177
         "used %d\n",
1626
177
         (long long)iTOW, flags,
1627
177
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1628
177
         session->newdata.latitude,
1629
177
         session->newdata.longitude,
1630
177
         session->newdata.altHAE,
1631
177
         session->newdata.track,
1632
177
         session->newdata.speed,
1633
177
         session->newdata.climb,
1634
177
         session->newdata.mode,
1635
177
         session->newdata.status,
1636
177
         session->gpsdata.satellites_used);
1637
177
    GPSD_LOG(LOG_IO, &session->context->errout,
1638
177
             "UBX: HNR-PVT: gpsFix:%s flags:%s valid:%s\n",
1639
177
             val2str(gpsFix, vpvt_fixType),
1640
177
             flags2str(flags, fhnr_pvt_flags, buf2, sizeof(buf2)),
1641
177
             flags2str(valid, fpvt_valid, buf3, sizeof(buf3)));
1642
1643
177
    return mask;
1644
177
}
1645
1646
/* UBX-INF-*
1647
 *
1648
 * Present in:
1649
 *   protVer 13 (6-series)
1650
 *   to
1651
 *   protVer 34 (10-series)
1652
 */
1653
static gps_mask_t ubx_msg_inf(struct gps_device_t *session,
1654
                              unsigned char *buf, size_t data_len)
1655
241
{
1656
241
    unsigned msgid = getbes16(buf, 2);
1657
1658
    // No minimum payload length
1659
1660
241
    if (data_len > MAX_PACKET_LENGTH - 1) {
1661
0
        data_len = MAX_PACKET_LENGTH - 1;
1662
0
    }
1663
1664
241
    GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: %s: %.*s\n",
1665
241
             val2str(msgid, vinf_ids),
1666
241
             (int)data_len, (char *)buf + UBX_PREFIX_LEN);
1667
241
    return 0;
1668
241
}
1669
1670
/**
1671
 * UBX-LOG-BATCH entry only part of UBX protocol
1672
 * Used for GPS standalone operation (internal batch retrieval)
1673
 */
1674
static gps_mask_t ubx_msg_log_batch(struct gps_device_t *session,
1675
                                    unsigned char *buf UNUSED, size_t data_len)
1676
159
{
1677
159
    struct tm unpacked_date = {0};
1678
159
    unsigned char contentValid, timeValid, flags, psmState;
1679
159
    bool gnssFixOK, diffSoln;
1680
159
    char ts_buf[TIMESPEC_LEN];
1681
159
    gps_mask_t mask = 0;
1682
1683
159
    gps_clear_log(&session->gpsdata.log);
1684
    // u-blox 8 100 bytes payload
1685
159
    if (100 > data_len) {
1686
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
1687
3
                 "UBX: LOG-BATCH: runt len %zd", data_len);
1688
3
        return 0;
1689
3
    }
1690
156
    timeValid = getub(buf, 15);
1691
156
    if (3 != (timeValid & 3)) {
1692
        // No time, pointless...
1693
1
        return 0;
1694
1
    }
1695
1696
155
    unpacked_date.tm_year = getleu16(buf, 8) - 1900;
1697
155
    unpacked_date.tm_mon = getub(buf, 10) - 1;
1698
155
    unpacked_date.tm_mday = getub(buf, 11);
1699
155
    unpacked_date.tm_hour = getub(buf, 12);
1700
155
    unpacked_date.tm_min = getub(buf, 13);
1701
155
    unpacked_date.tm_sec = getub(buf, 14);
1702
1703
155
    contentValid = getub(buf, 1);
1704
155
    session->gpsdata.log.index_cnt = getleu16(buf, 2);
1705
1706
155
    session->gpsdata.log.then.tv_sec = mkgmtime(&unpacked_date);
1707
155
    session->gpsdata.log.then.tv_nsec = getles32(buf, 20);
1708
155
    TS_NORM(&session->gpsdata.log.then);
1709
1710
155
    session->gpsdata.log.fixType = getub(buf, 24);
1711
155
    flags = getub(   buf, 25);
1712
155
    gnssFixOK = flags & 1;
1713
155
    diffSoln = flags & 2;
1714
155
    psmState = ((flags >> 2) & 7);
1715
1716
    // flags2 undocumented
1717
    // flags2 = getub(   buf, 26);
1718
1719
155
    if ((gnssFixOK &&
1720
95
         2 <= session->gpsdata.log.fixType)) {
1721
        // good 2D fix
1722
43
        session->gpsdata.log.lon = 1.0e-7 * getles32(buf, 28);
1723
43
        session->gpsdata.log.lat = 1.0e-7 * getles32(buf, 32);
1724
43
        session->gpsdata.log.gSpeed = 1.0e-3 * getles32(buf, 64);
1725
        // seems to be true heading
1726
43
        session->gpsdata.log.heading = 1.0e-5 * getles32(buf, 68);
1727
43
        if (diffSoln) {
1728
32
            session->gpsdata.log.status = STATUS_DGPS;
1729
32
        } else {
1730
11
            session->gpsdata.log.status = STATUS_GPS;
1731
11
        }
1732
43
        if (3 <= session->gpsdata.log.fixType) {
1733
            // good 3D fix
1734
42
            session->gpsdata.log.altHAE = 1.0e-3 * getles32(buf, 36);
1735
42
        }
1736
43
    }
1737
155
    session->gpsdata.log.hAcc = 1.0e-3 * getleu32(buf, 44);
1738
1739
155
    GPSD_LOG(LOG_INF, &session->context->errout,
1740
155
            "UBX: LOG-BATCH: time=%s index_cnt=%u fixType=%u lon=%.7f lat=%.7f"
1741
155
             " gSpeed=%.3f heading=%.5f altHae=%.3f psmState=%u hAcc=%.3f\n",
1742
155
             timespec_str(&session->gpsdata.log.then, ts_buf, sizeof(ts_buf)),
1743
155
             session->gpsdata.log.index_cnt, session->gpsdata.log.fixType,
1744
155
             session->gpsdata.log.lon, session->gpsdata.log.lat,
1745
155
             session->gpsdata.log.gSpeed, session->gpsdata.log.heading,
1746
155
             session->gpsdata.log.altHAE, psmState,
1747
155
             session->gpsdata.log.hAcc);
1748
1749
1750
155
    if (1 == (contentValid & 1)) {
1751
        // extraPVT valid
1752
        //  iTOW = getleu32(buf, 4);
1753
91
        session->gpsdata.log.tAcc = (double)getleu32(buf, 16);
1754
91
        session->gpsdata.log.numSV = getub(buf, 27);
1755
91
        session->gpsdata.log.altMSL = 1.0e-3 * getles32(buf, 40);
1756
91
        session->gpsdata.log.vAcc = 1.0e-3 * getleu32(buf, 48);
1757
91
        session->gpsdata.log.velN = 1.0e-3 * getles32(buf, 52);
1758
91
        session->gpsdata.log.velE = 1.0e-3 * getles32(buf, 56);
1759
91
        session->gpsdata.log.velD = 1.0e-3 * getles32(buf, 60);
1760
91
        session->gpsdata.log.sAcc = 1.0e-3 * getleu32(buf, 72);
1761
91
        session->gpsdata.log.headAcc = 1.0e-5 * getleu32(buf, 76);
1762
91
        session->gpsdata.log.pDOP = 1.0e-2 * getleu32(buf, 80);
1763
91
        GPSD_LOG(LOG_INF, &session->context->errout,
1764
91
                "UBX: LOG-BATCH extraPVT: time=%s index_cnt=%d"
1765
91
                 " tAcc=%.2f numSV=%d altMSL=%.3f hAcc=%.2f vAcc=%.3f"
1766
91
                 " velN=%.3f velE=%.3f velD=%.3f sAcc=%.3f headAcc=%.5f"
1767
91
                 " pDOP=%.5f\n",
1768
91
                 timespec_str(&session->gpsdata.log.then, ts_buf,
1769
91
                              sizeof(ts_buf)),
1770
91
                 session->gpsdata.log.index_cnt,
1771
91
                 session->gpsdata.log.tAcc, session->gpsdata.log.numSV,
1772
91
                 session->gpsdata.log.altMSL, session->gpsdata.log.hAcc,
1773
91
                 session->gpsdata.log.vAcc, session->gpsdata.log.velN,
1774
91
                 session->gpsdata.log.velE, session->gpsdata.log.velD,
1775
91
                 session->gpsdata.log.sAcc, session->gpsdata.log.headAcc,
1776
91
                 session->gpsdata.log.pDOP);
1777
91
    }
1778
1779
155
    if (2 == (contentValid & 2)) {
1780
102
        session->gpsdata.log.distance = getleu32(buf, 84);
1781
102
        session->gpsdata.log.totalDistance = getleu32(buf, 88);
1782
102
        session->gpsdata.log.distanceStd = getleu32(buf, 92);
1783
102
        GPSD_LOG(LOG_INF, &session->context->errout,
1784
102
                 "UBX: LOG-BATCH extraOdo: time=%s index_cnt=%d distance=%.0f"
1785
102
                 " totalDistance=%.0f distanceStd=%.0f\n",
1786
102
                 timespec_str(&session->gpsdata.log.then, ts_buf,
1787
102
                              sizeof(ts_buf)),
1788
102
                 session->gpsdata.log.index_cnt, session->gpsdata.log.distance,
1789
102
                 session->gpsdata.log.totalDistance,
1790
102
                 session->gpsdata.log.distanceStd);
1791
102
    }
1792
1793
155
    mask |= LOG_SET;
1794
155
    return mask;
1795
156
}
1796
1797
/**
1798
 * UBX-LOG-INFO info of log status
1799
 * u-blox 7,8,9.  protVer 14 to 29
1800
 * WIP: Initial decode, log only.
1801
 *
1802
 */
1803
static gps_mask_t ubx_msg_log_info(struct gps_device_t *session,
1804
                                   unsigned char *buf UNUSED, size_t data_len)
1805
16
{
1806
16
    struct tm oldest_date = {0}, newest_date = {0};
1807
16
    timespec_t oldest = {0, 0};
1808
16
    timespec_t newest = {0, 0};
1809
16
    unsigned char version, status;
1810
16
    unsigned long filestoreCapacity;
1811
16
    unsigned long currentMaxLogSize;
1812
16
    unsigned long currentLogSize;
1813
16
    unsigned long entryCount;
1814
16
    char ts_buf[TIMESPEC_LEN];
1815
16
    char ts_buf1[TIMESPEC_LEN];
1816
16
    gps_mask_t mask = 0;
1817
1818
16
    gps_clear_log(&session->gpsdata.log);
1819
    // u-blox 7/8/9 48 bytes payload
1820
16
    if (48 > data_len) {
1821
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
1822
2
                 "UBX: LOG-INFO: runt len %zd", data_len);
1823
2
        return 0;
1824
2
    }
1825
    // u-blox 7/8/9 version 1
1826
14
    version = getub(buf, 0);
1827
14
    filestoreCapacity = getleu32(buf, 4);
1828
14
    currentMaxLogSize = getleu32(buf, 16);
1829
14
    currentLogSize = getleu32(buf, 20);
1830
14
    entryCount = getleu32(buf, 24);
1831
14
    status = getub(buf, 44);
1832
1833
14
    oldest_date.tm_year = getleu16(buf, 28);
1834
14
    if (0 != oldest_date.tm_year) {
1835
13
        oldest_date.tm_year -= 1900;
1836
13
        oldest_date.tm_mon = getub(buf, 30) - 1;
1837
13
        oldest_date.tm_mday = getub(buf, 31);
1838
13
        oldest_date.tm_hour = getub(buf, 32);
1839
13
        oldest_date.tm_min = getub(buf, 33);
1840
13
        oldest_date.tm_sec = getub(buf, 34);
1841
13
        oldest.tv_sec = mkgmtime(&oldest_date);
1842
13
        oldest.tv_nsec = 0;
1843
13
        TS_NORM(&oldest);
1844
13
    }
1845
1846
14
    newest_date.tm_year = getleu16(buf, 36);
1847
14
    if (0 != newest_date.tm_year) {
1848
13
        newest_date.tm_year -= 1900;
1849
13
        newest_date.tm_mon = getub(buf, 38) - 1;
1850
13
        newest_date.tm_mday = getub(buf, 39);
1851
13
        newest_date.tm_hour = getub(buf, 40);
1852
13
        newest_date.tm_min = getub(buf, 41);
1853
13
        newest_date.tm_sec = getub(buf, 42);
1854
13
        newest.tv_sec = mkgmtime(&newest_date);
1855
13
        newest.tv_nsec = 0;
1856
13
        TS_NORM(&newest);
1857
13
    }
1858
1859
14
    GPSD_LOG(LOG_INF, &session->context->errout,
1860
14
             "UBX: LOG-INFO: version=%u status=x%x Cap=%lu MaxSize=%lu "
1861
14
             "Size=%lu cnt=%lu oldest=%s newest=%s\n",
1862
14
             version, status,
1863
14
             filestoreCapacity,
1864
14
             currentMaxLogSize,
1865
14
             currentLogSize,
1866
14
             entryCount,
1867
14
             timespec_str(&oldest, ts_buf, sizeof(ts_buf)),
1868
14
             timespec_str(&newest, ts_buf1, sizeof(ts_buf1)));
1869
1870
    // mask |= LOG_SET;
1871
14
    return mask;
1872
16
}
1873
1874
/*
1875
 * UBX-LOG-RETRIEVEPOS (Indexed PVT entry)
1876
 * Used for GPS standalone operation and host saved logs
1877
 * u-blox 7,8,9.  protVer 14 to 29
1878
 */
1879
static gps_mask_t ubx_msg_log_retrievepos(struct gps_device_t *session,
1880
                                          unsigned char *buf UNUSED,
1881
                                          size_t data_len)
1882
20
{
1883
20
    struct tm unpacked_date = {0};
1884
20
    unsigned char fixType;
1885
20
    gps_mask_t mask = 0;
1886
1887
20
    gps_clear_log(&session->gpsdata.log);
1888
    // u-blox 40 bytes payload
1889
20
    if (40 > data_len) {
1890
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
1891
2
                 "UBX: LOG-RETRIEVEPOS: runt len %zd", data_len);
1892
2
        return 0;
1893
2
    }
1894
18
    unpacked_date.tm_year = getleu16(buf, 30);
1895
18
    if (1900 > unpacked_date.tm_year) {
1896
        // useless, no date
1897
5
        return 0;
1898
5
    }
1899
13
    unpacked_date.tm_year -= 1900;
1900
13
    unpacked_date.tm_mon = getub(buf, 32) - 1;
1901
13
    unpacked_date.tm_mday = getub(buf, 33);
1902
13
    unpacked_date.tm_hour = getub(buf, 34);
1903
13
    unpacked_date.tm_min = getub(buf, 35);
1904
13
    unpacked_date.tm_sec = getub(buf, 36);
1905
13
    session->gpsdata.log.then.tv_sec = mkgmtime(&unpacked_date);
1906
1907
13
    session->gpsdata.log.index_cnt = getleu32(buf, 0);
1908
13
    session->gpsdata.log.lon = getleu32(buf, 4) * 1.0e-7;
1909
13
    session->gpsdata.log.lat = getleu32(buf, 8) * 1.0e-7;
1910
13
    session->gpsdata.log.altMSL = getleu32(buf, 12) * 1.0e-3;
1911
    // hAcc CEP() unspecified...
1912
13
    session->gpsdata.log.hAcc = getleu32(buf, 16) * 1.0e-3;
1913
13
    session->gpsdata.log.gSpeed = getleu32(buf, 20) * 1.0e-3;
1914
    // seems to be true heading
1915
13
    session->gpsdata.log.heading = getleu32(buf, 24) * 1.0e-5;
1916
13
    fixType = getub(buf, 29);
1917
13
    session->gpsdata.log.numSV = getub(buf, 38);
1918
1919
13
    switch (fixType) {
1920
1
    case 1:
1921
        // doc is unclear: 2D or 3D?
1922
1
        session->gpsdata.log.fixType = MODE_3D;
1923
1
        session->gpsdata.log.status = STATUS_DR;
1924
1
        break;
1925
1
    case 2:
1926
1
        session->gpsdata.log.fixType = MODE_2D;
1927
1
        session->gpsdata.log.status = STATUS_GPS;
1928
1
        break;
1929
1
    case 3:
1930
1
        session->gpsdata.log.fixType = MODE_3D;
1931
1
        session->gpsdata.log.status = STATUS_GPS;
1932
1
        break;
1933
0
    case 4:
1934
        // doc is unclear: 2D or 3D?
1935
0
        session->gpsdata.log.fixType = MODE_3D;
1936
0
        session->gpsdata.log.status = STATUS_GNSSDR;
1937
0
        break;
1938
1939
2
    case 0:
1940
2
        FALLTHROUGH
1941
10
    default:
1942
        // huh?
1943
10
        session->gpsdata.log.fixType = MODE_NO_FIX;
1944
10
        session->gpsdata.log.status = STATUS_UNK;
1945
10
        break;
1946
13
    }
1947
1948
    // (long long) because of time_t
1949
13
    GPSD_LOG(LOG_INF, &session->context->errout,
1950
13
             "UBX: LOG-RETRIEVEPOS: time=%lld entryIndex=%d"
1951
13
             " lon=%.7f lat=%.7f altMSL=%.3f hAcc=%.3f"
1952
13
             " gspeed=%.3f heading=%.5f fixType=%d numSV=%d\n",
1953
13
             (long long)session->gpsdata.log.then.tv_sec,
1954
13
             session->gpsdata.log.index_cnt, session->gpsdata.log.lon,
1955
13
             session->gpsdata.log.lat, session->gpsdata.log.altMSL,
1956
13
             session->gpsdata.log.hAcc, session->gpsdata.log.gSpeed,
1957
13
             session->gpsdata.log.heading, session->gpsdata.log.fixType,
1958
13
             session->gpsdata.log.numSV);
1959
1960
1961
13
    mask |= LOG_SET;
1962
13
    return mask;
1963
13
}
1964
1965
/*
1966
 * UBX-LOG-RETRIEVEPOSEXTRA (Indexed Odometry entry)
1967
 * Used for GPS standalone operation and host saved logs
1968
 * u-blox 7,8,9.  protVer 14 to 29
1969
 */
1970
static gps_mask_t ubx_msg_log_retrieveposextra(struct gps_device_t *session,
1971
                                               unsigned char *buf UNUSED,
1972
                                               size_t data_len)
1973
14
{
1974
14
    struct tm unpacked_date = {0};
1975
14
    gps_mask_t mask = 0;
1976
1977
14
    gps_clear_log(&session->gpsdata.log);
1978
    // u-blox 32 bytes payload
1979
14
    if (32 > data_len) {
1980
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
1981
1
                 "UBX: LOG-RETRIEVEPOSEXTRA: runt len %zd", data_len);
1982
1
        return 0;
1983
1
    }
1984
1985
13
    unpacked_date.tm_year = getleu16(buf, 6);
1986
13
    if (1900 > unpacked_date.tm_year) {
1987
        // useless, no date
1988
6
        return 0;
1989
6
    }
1990
7
    unpacked_date.tm_year -= 1900;
1991
7
    unpacked_date.tm_mon = getub(buf, 8) - 1;
1992
7
    unpacked_date.tm_mday = getub(buf, 9);
1993
7
    unpacked_date.tm_hour = getub(buf, 10);
1994
7
    unpacked_date.tm_min = getub(buf, 11);
1995
7
    unpacked_date.tm_sec = getub(buf, 12);
1996
1997
7
    session->gpsdata.log.then.tv_sec = mkgmtime(&unpacked_date);
1998
7
    session->gpsdata.log.index_cnt = getleu32(buf, 0);
1999
    // distance units undocumented!  Assume meters, as in UBX-LOG-BATCH
2000
7
    session->gpsdata.log.distance = (double)getleu32(buf, 16);
2001
2002
    // (long long) because of time_t
2003
7
    GPSD_LOG(LOG_INF, &session->context->errout,
2004
7
             "UBX: LOG-RETRIEVEPOSEXTRA:"
2005
7
             " time=%lld entryindex=%u distance=%.0f\n",
2006
7
             (long long)session->gpsdata.log.then.tv_sec,
2007
7
             session->gpsdata.log.index_cnt, session->gpsdata.log.distance);
2008
2009
7
    mask |= LOG_SET;
2010
7
    return mask;
2011
13
}
2012
2013
/*
2014
 * UBX-LOG-RETRIEVESTRING
2015
 * Used for GPS standalone operation and host saved logs
2016
 * u-blox 7,8,9.  protVer 14 to 29
2017
 */
2018
static gps_mask_t ubx_msg_log_retrievestring(struct gps_device_t *session,
2019
                                             unsigned char *buf UNUSED,
2020
                                             size_t data_len)
2021
20
{
2022
20
    struct tm unpacked_date = {0};
2023
20
    unsigned int byteCount;
2024
20
    gps_mask_t mask = 0;
2025
2026
20
    gps_clear_log(&session->gpsdata.log);
2027
    // u-blox 16+ bytes payload
2028
20
    if (16 > data_len) {
2029
4
        GPSD_LOG(LOG_WARN, &session->context->errout,
2030
4
                 "UBX: LOG-RETRIEVESTRING: runt len %zd", data_len);
2031
4
        return 0;
2032
4
    }
2033
2034
16
    unpacked_date.tm_year = getleu16(buf, 6);
2035
16
    if (1900 > unpacked_date.tm_year) {
2036
        // useless, no date
2037
4
        return 0;
2038
4
    }
2039
12
    unpacked_date.tm_year -= 1900;
2040
12
    unpacked_date.tm_mon = getub(buf, 8) - 1;
2041
12
    unpacked_date.tm_mday = getub(buf, 9);
2042
12
    unpacked_date.tm_hour = getub(buf, 10);
2043
12
    unpacked_date.tm_min = getub(buf, 11);
2044
12
    unpacked_date.tm_sec = getub(buf, 12);
2045
2046
12
    session->gpsdata.log.then.tv_sec = mkgmtime(&unpacked_date);
2047
12
    session->gpsdata.log.index_cnt = getleu32(buf, 0);
2048
12
    byteCount = getleu16(buf, 14);
2049
2050
    // string could be 0 to 256 bytes, plus NUL
2051
12
    (void)strlcpy(session->gpsdata.log.string, (const char*)&buf[16],
2052
12
                  sizeof(session->gpsdata.log.string));
2053
    // (long long) because of time_t
2054
12
    GPSD_LOG(LOG_INF, &session->context->errout,
2055
12
             "UBX: LOG-RETRIEVESTRING:"
2056
12
             " time=%lld entryindex=%u byteCount=%u string=%s\n",
2057
12
             (long long)session->gpsdata.log.then.tv_sec,
2058
12
             session->gpsdata.log.index_cnt,
2059
12
             byteCount, session->gpsdata.log.string);
2060
2061
12
    mask |= LOG_SET;
2062
12
    return mask;
2063
16
}
2064
2065
/* UBX-MON-COMMS
2066
 * Replacement for MON-RXBUF and MON-TXBUF
2067
 */
2068
static gps_mask_t ubx_msg_mon_comms(struct gps_device_t *session,
2069
                                    unsigned char *buf, size_t data_len)
2070
32
{
2071
32
    gps_mask_t mask = 0;
2072
32
    char buf2[80];
2073
32
    unsigned i;
2074
32
    unsigned version;
2075
32
    unsigned nPorts;
2076
32
    unsigned txErrors;
2077
32
    unsigned protIds[4];
2078
2079
32
    if (8 > data_len) {
2080
        // 8 + (nPorts * 40)
2081
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
2082
2
                 "UBX: MON-COMMS: runt payload len %zd\n", data_len);
2083
2
        return 0;
2084
2
    }
2085
30
    version = getub(buf, 0);
2086
30
    if (0 != version) {
2087
5
        GPSD_LOG(LOG_WARN, &session->context->errout,
2088
5
                 "UBX: MON-COMMS unkwnown version %u\n", version);
2089
5
        return 0;
2090
5
    }
2091
25
    nPorts = getub(buf, 1);
2092
25
    if ((8 + (nPorts * 40)) > data_len) {
2093
        // 8 + (nPorts * 40)
2094
7
        GPSD_LOG(LOG_WARN, &session->context->errout,
2095
7
                 "UBX: MON-COMMS unkwnown runt %zd\n", data_len);
2096
7
        return 0;
2097
7
    }
2098
18
    txErrors = getub(buf, 2);
2099
90
    for (i = 0; i < 4; i++) {
2100
72
        protIds[i] = getub(buf, 3 + i);
2101
72
    }
2102
2103
18
    GPSD_LOG(LOG_PROG, &session->context->errout,
2104
18
             "UBX: MON-COMMS: version %u, nPorts %u txErrors x%x  "
2105
18
             "protIds %u %u %u %u\n",
2106
18
             version, nPorts, txErrors, protIds[0], protIds[1], protIds[2],
2107
18
             protIds[3]);
2108
18
    GPSD_LOG(LOG_IO, &session->context->errout,
2109
18
             "UBX: MON-COMMS: txErrors:%s protIds %s %s %s %s\n",
2110
18
             flags2str(txErrors, vmon_comms_txerrors, buf2, sizeof(buf2)),
2111
18
             val2str(protIds[0], vprotIds),
2112
18
             val2str(protIds[1], vprotIds),
2113
18
             val2str(protIds[2], vprotIds),
2114
18
             val2str(protIds[3], vprotIds));
2115
2116
228
    for (i = 0; i < nPorts; i++) {
2117
210
        unsigned portId = getleu16(buf, 8 + (i * 40));
2118
210
        unsigned txPending = getleu16(buf, 10 + (i * 40));
2119
210
        unsigned long txBytes = getleu32(buf, 12 + (i * 40));
2120
210
        unsigned txUsage = getub(buf, 16 + (i * 40));
2121
210
        unsigned txPeakUsage = getub(buf, 17 + (i * 40));
2122
210
        unsigned rxPending = getleu16(buf, 18 + (i * 40));
2123
210
        unsigned long rxBytes = getleu32(buf, 20 + (i * 40));
2124
210
        unsigned rxUsage = getub(buf, 24 + (i * 40));
2125
210
        unsigned rxPeakUsage = getub(buf, 25 + (i * 40));
2126
210
        unsigned overrunErrs = getleu16(buf, 26 + (i * 40));
2127
210
        unsigned long msgs = getleu32(buf, 28 + (i * 40));
2128
210
        unsigned long skipped = getleu32(buf, 44 + (i * 40));
2129
2130
210
        GPSD_LOG(LOG_IO, &session->context->errout,
2131
210
                 "UBX: MON-COMMS: portId:%s\n",
2132
210
                 val2str(portId, vtarget));
2133
210
        GPSD_LOG(LOG_PROG, &session->context->errout,
2134
210
                 "UBX: MON-COMMS: portId x%x, "
2135
210
                 "txPending %u txBytes %lu txUsage %u%% txPeakUsage %u%% "
2136
210
                 "rxPending %u rxBytes %lu rxUsage %u%% rxPeakUsage %u%% "
2137
210
                 "overrunErrs %u msgs %lu skipped %lu\n",
2138
210
                 portId,
2139
210
                 txPending, txBytes, txUsage, txPeakUsage,
2140
210
                 rxPending, rxBytes, rxUsage, rxPeakUsage,
2141
210
                 overrunErrs, msgs, skipped);
2142
210
    }
2143
18
    return mask;
2144
25
}
2145
2146
/* UBX-MON-HW
2147
 * 68 bytes in protVer 12 ( 6-series)
2148
 *    Present from Antaris (4-series)
2149
 * 60 bytes in 8-series and 9-series
2150
 *    Deprecated in protVer 32. M9 and 10-series, use MON-HW and MON-RF
2151
 * 56 bytes in protVer 34 (10-series)
2152
 *    Deprecated. and undocumented,  on M10, use MON-HW and MON-RF
2153
 *
2154
 * Oddly, UBX-MON-HW is output after NAV-EOE.  So too lare for the one
2155
 * TPV for that epoch, and too early for the next epoch.
2156
 */
2157
static gps_mask_t ubx_msg_mon_hw(struct gps_device_t *session,
2158
                                 unsigned char *buf, size_t data_len)
2159
13
{
2160
13
    char buf2[80];
2161
13
    unsigned int noisePerMs;
2162
13
    unsigned int agcCnt;
2163
13
    unsigned int aStatus;
2164
13
    unsigned int aPower;
2165
13
    unsigned int flags;
2166
13
    unsigned int jamInd;
2167
13
    gps_mask_t mask = 0;
2168
2169
13
    if (60 > data_len) {
2170
        // Doc says 68, but 8-series can have 60
2171
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
2172
3
                 "UBX: MON-HW: runt payload len %zd\n", data_len);
2173
3
        return 0;
2174
3
    }
2175
2176
10
    noisePerMs = getleu16(buf, 16);
2177
10
    agcCnt = getleu16(buf, 18);         // 0 to 8191
2178
10
    aStatus = getub(buf, 20);
2179
10
    aPower = getub(buf, 21);
2180
    /* flags:
2181
     * 5 only has rtcCalib
2182
     * 6 (6.03) adds safeBoot
2183
     * 6 (7.03) adds jammingState
2184
     * 9 adds xtalAbsent
2185
     */
2186
10
    flags = getub(buf, 22);
2187
    // VP, 17 bytes on protVer 15+
2188
    // VP, 25 bytes on u-blox 6
2189
    // jamInd, on 5 this is reserved
2190
10
    if (68 == data_len) {
2191
1
        jamInd = getub(buf, 53);
2192
9
    } else if (60 == data_len) {
2193
6
        jamInd = getub(buf, 45);
2194
6
    } else {
2195
        // probably 56 == data_len, undocuemted in M10
2196
3
        jamInd = 0;   // WTF?
2197
3
    }
2198
10
    session->newdata.jam = jamInd;
2199
10
    session->newdata.ant_stat = antStat2ant_status(aStatus);
2200
10
    session->newdata.ant_power = aPower2ant_power(aPower);
2201
2202
10
    if (0 < jamInd ||
2203
7
        ANT_OK <= session->newdata.ant_stat ||
2204
8
        ANT_PWR_UNK != session->newdata.ant_power) {
2205
8
        mask |= REPORT_IS;           // force a new, extra, TPV.
2206
8
    }
2207
2208
10
    GPSD_LOG(LOG_PROG, &session->context->errout,
2209
10
             "UBX: MON-HW: noisePerMs %u, agcCmt %u aStatus %u aPower %u "
2210
10
             "flags x%x jamInd %u\n",
2211
10
             noisePerMs, agcCnt, aStatus, aPower, flags, jamInd);
2212
10
    GPSD_LOG(LOG_IO, &session->context->errout,
2213
10
             "UBX: MON-HW:aStatus:%s aPower:%s flags:%s\n",
2214
10
             val2str(aStatus, vaStatus),
2215
10
             val2str(aPower, vaPower),
2216
10
             flags2str(flags, vmon_hw_flags, buf2, sizeof(buf2)));
2217
10
    return mask;
2218
13
}
2219
2220
/* UBX-MON-RF
2221
 * Present in protVer 27+ (9-series)
2222
 * Partially replaces MON-HW
2223
 *
2224
 * Oddly, UBX-MON-RF is output after NAV-EOE.  So too lare for the one
2225
 * TPV for that epoch, and too early for the next epoch.
2226
 */
2227
static gps_mask_t ubx_msg_mon_rf(struct gps_device_t *session,
2228
                                 unsigned char *buf, size_t data_len)
2229
59
{
2230
59
    unsigned i;
2231
59
    gps_mask_t mask = 0;
2232
59
    unsigned version;
2233
59
    unsigned nBlocks;
2234
59
    unsigned blockSize = 0;
2235
59
    bool compact;
2236
2237
59
    if (4 > data_len) {
2238
        // 4 + (nBlocks * 24)
2239
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
2240
2
                 "UBX: MON-RF: runt payload len %zd\n", data_len);
2241
2
        return 0;
2242
2
    }
2243
57
    version = getub(buf, 0);
2244
57
    nBlocks = getub(buf, 1);
2245
2246
57
    if (0 != version) {
2247
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2248
1
                 "UBX: MON-RF unkwnown version %u\n", version);
2249
1
        return 0;
2250
1
    }
2251
56
    if (0 == nBlocks) {
2252
        // avoid divide by zero
2253
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2254
1
                 "UBX: MON-RF bBlocks is zero\n");
2255
1
        return 0;
2256
1
    }
2257
55
    blockSize = (data_len - 4) / nBlocks;
2258
2259
55
    GPSD_LOG(LOG_PROG, &session->context->errout,
2260
55
             "UBX: MON-RF: version %u, nblocks %u blockSize %u\n",
2261
55
             version, nBlocks, blockSize);
2262
2263
55
    if (4 != (data_len - blockSize * nBlocks)) {
2264
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2265
1
                 "UBX-MON-RF:  Bad length %zu s/b %u, nBlocks %u]n",
2266
1
                 data_len, 4 + blockSize * nBlocks, nBlocks);
2267
1
        return 0;
2268
1
    }
2269
2270
54
    if (20 == blockSize) {
2271
        // ZED-F9R HPS 1.30 firmware
2272
19
        compact = true;
2273
35
    } else if (24 == blockSize) {
2274
22
        compact = false;
2275
22
    } else {
2276
13
        GPSD_LOG(LOG_WARN, &session->context->errout,
2277
13
                 "UBX: MON-RF: bad blockSize%u\n", blockSize);
2278
13
        return 0;
2279
13
    }
2280
500
    for (i = 0; i < nBlocks; i++){
2281
459
        if (false == compact) {
2282
            /* ZED-F9N 1 == nBlock
2283
             * ZED-F9P 2 == nBlock
2284
             * what to do with with two jamInd and two antStatus? */
2285
252
            unsigned off = i * 24;
2286
252
            unsigned blockId = getub(buf, 4 + off);
2287
252
            unsigned flags =  getub(buf, 5 + off);
2288
252
            unsigned jammingState = flags & 3;
2289
252
            unsigned antStatus = getub(buf, 6 + off);
2290
252
            unsigned antPower = getub(buf, 7 + off);
2291
252
            unsigned long postStatus = getleu32(buf, 8 + off);
2292
252
            unsigned long reserved1 = getleu32(buf, 12 + off);
2293
252
            unsigned agcCnt = getleu16(buf, 18 + off);         // 0 to 8191
2294
252
            unsigned jamInd = getub(buf, 20 + off);   // aka cwsuppression
2295
252
            int ofsI = getsb(buf, 21 + off);
2296
252
            unsigned magI = getub(buf, 22 + off);
2297
252
            int ofsQ = getsb(buf, 23 + off);
2298
252
            unsigned magQ = getub(buf, 24 + off);
2299
252
            unsigned reserved2 = getleu16(buf, 25 + off);
2300
252
            unsigned ant_stat;
2301
2302
252
            ant_stat = antStat2ant_status(antStatus);
2303
2304
            // use the highest ant_stat and jamInd
2305
252
            if ((unsigned)session->newdata.ant_stat < ant_stat) {
2306
14
                session->newdata.ant_stat = ant_stat;
2307
14
            }
2308
2309
252
            session->newdata.ant_power = aPower2ant_power(antPower);
2310
2311
252
            if ((unsigned)session->newdata.jam < jamInd) {
2312
48
                session->newdata.jam = jamInd;
2313
48
            }
2314
2315
252
            GPSD_LOG(LOG_PROG, &session->context->errout,
2316
252
                     "UBX: MON-RF: blk %u flags x%x jammingState %u "
2317
252
                     "antStatus %u antPower %u postStatus %lu reserved1 x%lx "
2318
252
                     "ageCnt %u "
2319
252
                     "jamInd %u ofsI %d magI %u ofsI %d magQ %u "
2320
252
                     "reserved2 x%x\n",
2321
252
                     blockId, flags, jammingState, antStatus, antPower,
2322
252
                     postStatus, reserved1, agcCnt, jamInd,
2323
252
                     ofsI, magI, ofsQ, magQ, reserved2);
2324
252
            GPSD_LOG(LOG_IO, &session->context->errout,
2325
252
                     "UBX: MON-RF:    blockId (%s) flags (%s) antStatus (%s) "
2326
252
                     "antPower (%s) agc %.1f%%\n",
2327
252
                     val2str(blockId, vmon_rf_blockId),
2328
252
                     val2str(flags, vmon_rf_flags),
2329
252
                     val2str(antStatus, vaStatus),
2330
252
                     val2str(antPower, vaPower), agcCnt / 81.91);
2331
252
        } else {
2332
            // compact, 20 bytes, HPS 1.30
2333
            /* ZED-F9R 2 == nBlock
2334
             * what to do with with two jamInd and two antStatus? */
2335
207
            unsigned off = i * 20;
2336
207
            unsigned blockId = getub(buf, 4 + off);
2337
207
            unsigned antStatus = getub(buf, 5 + off);
2338
207
            unsigned antPower = getub(buf, 6 + off);
2339
207
            unsigned cwSuppression = getub(buf, 7 + off);
2340
207
            unsigned long postStatus = getleu32(buf, 8 + off);
2341
            // reserved1 4 bytes
2342
207
            unsigned noisePerMS = getleu16(buf, 16 + off);
2343
207
            unsigned agcCnt = getleu16(buf, 18 + off);         // 0 to 8191
2344
207
            int ofsI = getsb(buf, 20 + off);
2345
207
            unsigned magI = getub(buf, 21 + off);
2346
207
            int ofsQ = getsb(buf, 22 + off);
2347
207
            unsigned magQ = getub(buf, 23 + off);
2348
207
            unsigned ant_stat;
2349
2350
207
            ant_stat = antStat2ant_status(antStatus);
2351
2352
            // use the highest ant_stat and jamInd
2353
207
            if ((unsigned)session->newdata.ant_stat < ant_stat) {
2354
15
                session->newdata.ant_stat = ant_stat;
2355
15
            }
2356
207
            session->newdata.ant_power = aPower2ant_power(antPower);
2357
2358
207
            GPSD_LOG(LOG_PROG, &session->context->errout,
2359
207
                     "UBX: MON-RF: blk %u antStatus %u antPower %u "
2360
207
                     "cwSuppression %u postStatus %lu ageCnt %u jamInd %u "
2361
207
                     "ofsI %d magI %u ofsI %d magQ %u\n",
2362
207
                     blockId, antStatus, antPower, cwSuppression,
2363
207
                     postStatus, noisePerMS,  agcCnt,
2364
207
                     ofsI, magI, ofsQ, magQ);
2365
207
            GPSD_LOG(LOG_IO, &session->context->errout,
2366
207
                     "UBX: MON-RF:    blockId (%s) antStatus (%s) "
2367
207
                     "antPower (%s) agc %.1f%%\n",
2368
207
                     val2str(blockId, vmon_rf_blockId),
2369
207
                     val2str(antStatus, vaStatus),
2370
207
                     val2str(antPower, vaPower),
2371
207
                     agcCnt / 81.91);
2372
207
        }
2373
459
    }
2374
41
    if (0 < session->newdata.jam ||
2375
20
        ANT_OK <= session->newdata.ant_stat ||
2376
34
        ANT_PWR_UNK != session->newdata.ant_power) {
2377
34
        mask |= REPORT_IS;           // force a new, extra, TPV.
2378
34
    }
2379
41
    return mask;
2380
54
}
2381
2382
/* UBX-MON-RXBUF
2383
 * Present in u-blox 5+ through at least protVer 23.01
2384
 * Supported but deprecated in M9P protVer 27.11, use MON-COMMS
2385
 * Supported but deprecated in M9N protVer 32.00 */
2386
static gps_mask_t ubx_msg_mon_rxbuf(struct gps_device_t *session,
2387
                                    unsigned char *buf, size_t data_len)
2388
2
{
2389
2
    int i;
2390
2391
2
    if (24 != data_len) {
2392
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2393
1
                 "UBX: MON-RXBUF: runt payload len %zd\n", data_len);
2394
1
        return 0;
2395
1
    }
2396
2397
7
    for (i = 0; i < 6; i++) {
2398
6
        unsigned int pending = getleu16(buf, i * 2);
2399
6
        unsigned int usage =  getub(buf, 12 + i);
2400
6
        unsigned int peakUsage = getub(buf, 18 + i);
2401
2402
6
        GPSD_LOG(LOG_IO, &session->context->errout,
2403
6
                 "UBX: MON-RXBUF: tgt:%s\n",
2404
6
                 val2str(i, vtarget));
2405
6
        GPSD_LOG(LOG_INF, &session->context->errout,
2406
6
                 "UBX: MON-RXBUF: tgt%d pending %4u usage %3u%% peakUsage %3d%%\n",
2407
6
                 i, pending, usage, peakUsage);
2408
6
    }
2409
1
    return 0;
2410
2
}
2411
2412
/* UBX-MON-TXBUF
2413
 * Present in u-blox 5+ through at least protVer 23.01
2414
 * Supported but deprecated in M9P protVer 27.11
2415
 * Supported but deprecated in M9N protVer 32.00 */
2416
static gps_mask_t ubx_msg_mon_txbuf(struct gps_device_t *session,
2417
                                    unsigned char *buf, size_t data_len)
2418
2
{
2419
2
    char buf2[80];
2420
2
    unsigned tUsage, tPeakusage;
2421
2
    unsigned errors, limit, reserved1;
2422
2
    int i;
2423
2424
2
    if (28 != data_len) {
2425
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2426
1
                 "UBX: MON-TXBUF: runt payload len %zd\n", data_len);
2427
1
        return 0;
2428
1
    }
2429
2430
1
    errors = limit = getub(buf, 26);
2431
2432
7
    for (i = 0; i < 6; i++) {
2433
6
        unsigned int pending = getleu16(buf, i * 2);
2434
6
        unsigned int usage =  getub(buf, 12 + i);
2435
6
        unsigned int peakUsage = getub(buf, 18 + i);
2436
2437
6
        GPSD_LOG(LOG_IO, &session->context->errout,
2438
6
                 "UBX: MON-TXBUF: tgt:%s\n",
2439
6
                 val2str(i, vtarget));
2440
6
        GPSD_LOG(LOG_INF, &session->context->errout,
2441
6
                 "UBX: MON-TXBUF: tgt %d limit %u pending %4u "
2442
6
                 "usage %3u%% peakUsage %3d%%\n",
2443
6
                 i, limit & 1, pending, usage, peakUsage);
2444
6
        limit = limit >> 1;
2445
6
    }
2446
1
    tUsage = getub(buf, 24);
2447
1
    tPeakusage = getub(buf, 25);
2448
1
    reserved1 = getub(buf, 27);
2449
2450
1
    GPSD_LOG(LOG_INF, &session->context->errout,
2451
1
             "UBX: MON-TXBUF: tUsage %3u%%, tPeakusage %3u%%, errors 0x%02x, "
2452
1
             "reserved1 0x%02x\n",
2453
1
             tUsage, tPeakusage, errors, reserved1);
2454
2455
1
    GPSD_LOG(LOG_IO, &session->context->errout,
2456
1
             "UBX: MON-TXBUF: errors:%s\n",
2457
1
             flags2str(errors, vmon_txbuf_errors, buf2, sizeof(buf2)));
2458
1
    return 0;
2459
2
}
2460
2461
/**
2462
 * Receiver/Software Version
2463
 * UBX-MON-VER
2464
 *
2465
 * sadly more info than fits in session->swtype for now.
2466
 * so squish the data hard.
2467
 */
2468
static gps_mask_t ubx_msg_mon_ver(struct gps_device_t *session,
2469
                                  unsigned char *buf,
2470
                                  size_t data_len)
2471
94
{
2472
94
    int n = 0;                           // extended info counter
2473
94
    int num_ext = (data_len - 40) / 30;  // number of extensions
2474
94
    char obuf[128];                      // temp version string buffer
2475
94
    char *cptr;
2476
2477
94
    if (40 > data_len) {
2478
4
        GPSD_LOG(LOG_WARN, &session->context->errout,
2479
4
                 "UBX: MON-VER: runt payload len %zd", data_len);
2480
4
        return 0;
2481
4
    }
2482
2483
    // save SW and HW Version as subtype
2484
90
    (void)snprintf(obuf, sizeof(obuf),
2485
90
                   "SW %.30s,HW %.10s",
2486
90
                   (char *)buf,
2487
90
                   (char *)(buf + 30));
2488
2489
    // save what we can
2490
90
    (void)strlcpy(session->subtype, obuf, sizeof(session->subtype));
2491
2492
90
    obuf[0] = '\0';
2493
    // extract Extended info strings.
2494
1.56k
    for (n = 0; n < num_ext; n++) {
2495
1.47k
        int start_of_str = 40 + (30 * n);
2496
2497
1.47k
        if (0 < n) {
2498
            // commas between elements
2499
1.39k
            (void)strlcat(obuf, ",", sizeof(obuf));
2500
1.39k
        }
2501
1.47k
        (void)strlcat(obuf, (char *)&buf[start_of_str], sizeof(obuf));
2502
1.47k
    }
2503
2504
    // save what we can in subtype1
2505
90
    (void)strlcpy(session->subtype1, obuf, sizeof(session->subtype1));
2506
2507
    // find PROTVER literal, followed by single separator character
2508
90
    cptr = strstr(obuf, "PROTVER=");     // protVer 18 and above
2509
90
    if (NULL == cptr) {
2510
78
        cptr = strstr(obuf, "PROTVER "); // protVer 17 and below
2511
78
    }
2512
90
    if (NULL != cptr) {
2513
41
        int protver = atoi(cptr + 8);
2514
41
        if (7 < protver) {
2515
            /* protver 8, u-blox Antaris, is the oldest we know, but never
2516
             * used explicitly.  protver 15, u-blox 8, is oldest seen. */
2517
5
            session->driver.ubx.protver = protver;
2518
5
        }
2519
41
    }
2520
2521
    /* MON-VER did not contain PROTVER in any extension field (typical for
2522
     * protVer < 15), so use mapping table to try to derive protVer from
2523
     * firmware revision number carried in swVersion field */
2524
90
    if (0 == session->driver.ubx.protver) {
2525
799
        for (n = 0; NULL != fw_protver_map[n].fw_string; n++) {
2526
            // skip "SW " prefix in session->subtype
2527
743
            cptr = strstr(session->subtype + 3, fw_protver_map[n].fw_string);
2528
            // use only when swVersion field starts with fw_string
2529
743
            if (cptr == (session->subtype + 3)) {
2530
12
                session->driver.ubx.protver =
2531
12
                    (unsigned char)fw_protver_map[n].protver;
2532
12
                break;
2533
12
            }
2534
743
        }
2535
68
        if (0 == session->driver.ubx.protver) {
2536
            // Still not found, old chip.  Set to one so we know we tried.
2537
56
            session->driver.ubx.protver = 1;
2538
56
        }
2539
68
    }
2540
2541
    // output SW and HW Version at LOG_INF
2542
90
    GPSD_LOG(LOG_INF, &session->context->errout,
2543
90
             "UBX: MON-VER: %s %s PROTVER %u\n",
2544
90
             session->subtype, session->subtype1,
2545
90
             session->driver.ubx.protver);
2546
2547
2548
90
    return 0;
2549
94
}
2550
2551
/**
2552
 * Clock Solution UBX-NAV-CLOCK
2553
 *
2554
 * Present in:
2555
 *     protVer 8 to 34 (Antaris 4 to M10)
2556
 */
2557
static gps_mask_t ubx_msg_nav_clock(struct gps_device_t *session,
2558
                                    unsigned char *buf, size_t data_len)
2559
20
{
2560
20
    unsigned long tAcc, fAcc;
2561
2562
20
    if (20 > data_len) {
2563
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2564
1
                 "UBX: NAV-CLOCK: runt payload len %zd", data_len);
2565
1
        return 0;
2566
1
    }
2567
2568
19
    session->driver.ubx.iTOW = getleu32(buf, 0);
2569
    // u-bloc 6 sets clockbias and clockdrift to 0
2570
19
    session->gpsdata.fix.clockbias = getles32(buf, 4);
2571
19
    session->gpsdata.fix.clockdrift = getles32(buf, 8);
2572
19
    tAcc = getleu32(buf, 12);
2573
19
    fAcc = getleu32(buf, 16);
2574
19
    GPSD_LOG(LOG_PROG, &session->context->errout,
2575
19
             "UBX: NAV-CLOCK: iTOW=%lld clkB %ld clkD %ld tAcc %lu fAcc %lu\n",
2576
19
             (long long)session->driver.ubx.iTOW,
2577
19
             session->gpsdata.fix.clockbias,
2578
19
             session->gpsdata.fix.clockdrift,
2579
19
             tAcc, fAcc);
2580
19
    return 0;
2581
20
}
2582
2583
/**
2584
 * DGPS Data Used for NAV
2585
 *
2586
 * May be good cycle ender
2587
 *
2588
 * Present in u-blox 7
2589
 */
2590
static gps_mask_t ubx_msg_nav_dgps(struct gps_device_t *session,
2591
                                   unsigned char *buf, size_t data_len)
2592
65
{
2593
65
    long age;
2594
2595
65
    if (16 > data_len) {
2596
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2597
1
                 "UBX: NAV-DGPS: runt payload len %zd", data_len);
2598
1
        return 0;
2599
1
    }
2600
2601
64
    session->driver.ubx.iTOW = getleu32(buf, 0);
2602
64
    age = getleu32(buf, 4);
2603
64
    GPSD_LOG(LOG_PROG, &session->context->errout,
2604
64
             "NAV-DGPS: iTOW=%lld age %ld\n",
2605
64
             (long long)session->driver.ubx.iTOW, age);
2606
64
    return 0;
2607
65
}
2608
2609
/**
2610
 * UBX-NAV-DOP, Dilution of precision message
2611
 *
2612
 * Present in all u-blox (4 to 10)
2613
 */
2614
static gps_mask_t ubx_msg_nav_dop(struct gps_device_t *session,
2615
                                  unsigned char *buf, size_t data_len)
2616
591
{
2617
591
    unsigned u;
2618
591
    gps_mask_t mask = 0;
2619
2620
591
    if (18 > data_len) {
2621
167
        GPSD_LOG(LOG_WARN, &session->context->errout,
2622
167
                 "UBX: NAV-DOP: runt payload len %zd", data_len);
2623
167
        return 0;
2624
167
    }
2625
2626
424
    session->driver.ubx.iTOW = getleu32(buf, 0);
2627
    /*
2628
     * We make a deliberate choice not to clear DOPs from the
2629
     * last skyview here, but rather to treat this as a supplement
2630
     * to our calculations from the visibility matrix, trusting
2631
     * the firmware algorithms over ours.
2632
     */
2633
424
    u = getleu16(buf, 4);
2634
424
    if (9999 > u) {
2635
168
        session->gpsdata.dop.gdop = (double)(u / 100.0);
2636
168
        mask |= DOP_SET;
2637
168
    }
2638
424
    u = getleu16(buf, 6);
2639
424
    if (9999 > u) {
2640
221
        session->gpsdata.dop.pdop = (double)(u / 100.0);
2641
221
        mask |= DOP_SET;
2642
221
    }
2643
424
    u = getleu16(buf, 8);
2644
424
    if (9999 > u) {
2645
199
        session->gpsdata.dop.tdop = (double)(u / 100.0);
2646
199
        mask |= DOP_SET;
2647
199
    }
2648
424
    u = getleu16(buf, 10);
2649
424
    if (9999 > u) {
2650
232
        session->gpsdata.dop.vdop = (double)(u / 100.0);
2651
232
        mask |= DOP_SET;
2652
232
    }
2653
424
    u = getleu16(buf, 12);
2654
424
    if (9999 > u) {
2655
222
        session->gpsdata.dop.hdop = (double)(u / 100.0);
2656
222
        mask |= DOP_SET;
2657
222
    }
2658
    // Northing DOP
2659
424
    u = getleu16(buf, 14);
2660
424
    if (9999 > u) {
2661
109
        session->gpsdata.dop.ydop = (double)(u / 100.0);
2662
109
        mask |= DOP_SET;
2663
109
    }
2664
    // Easting DOP
2665
424
    u = getleu16(buf, 16);
2666
424
    if (9999 > u) {
2667
175
        session->gpsdata.dop.xdop = (double)(u / 100.0);
2668
175
        mask |= DOP_SET;
2669
175
    }
2670
424
    GPSD_LOG(LOG_PROG, &session->context->errout,
2671
424
             "UBX: NAV-DOP: gdop=%.2f pdop=%.2f "
2672
424
             "hdop=%.2f vdop=%.2f tdop=%.2f ydop=%.2f xdop=%.2f\n",
2673
424
             session->gpsdata.dop.gdop,
2674
424
             session->gpsdata.dop.pdop,
2675
424
             session->gpsdata.dop.hdop,
2676
424
             session->gpsdata.dop.vdop,
2677
424
             session->gpsdata.dop.tdop,
2678
424
             session->gpsdata.dop.ydop,
2679
424
             session->gpsdata.dop.xdop);
2680
424
    return mask;
2681
591
}
2682
2683
/**
2684
 * Position error ellipse parameters
2685
 * protVer 19.1 and up
2686
 * Not in u-blox 5, 6 or 7
2687
 * Present in some u-blox 8, 9 and 10 (ADR, HPS)
2688
 */
2689
static gps_mask_t ubx_msg_nav_eell(struct gps_device_t *session,
2690
                                   unsigned char *buf, size_t data_len)
2691
54
{
2692
54
    unsigned version;
2693
54
    unsigned errEllipseOrient;
2694
54
    unsigned long errEllipseMajor, errEllipseMinor;
2695
2696
54
    if (16 > data_len) {
2697
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2698
1
                 "UBX: NAV-EELL: runt payload len %zd", data_len);
2699
1
        return 0;
2700
1
    }
2701
2702
53
    session->driver.ubx.iTOW = getleu32(buf, 0);
2703
53
    version = getub(buf, 4);
2704
53
    if (0 != version) {
2705
36
        GPSD_LOG(LOG_WARN, &session->context->errout,
2706
36
                 "UBX: NAV-EELL unknown version %u s/b 0", version);
2707
36
        return 0;
2708
36
    }
2709
17
    session->newdata.errEllipseOrient = (double)(getleu16(buf, 6) * 1e-2);
2710
17
    session->newdata.errEllipseMajor = (double)(getleu32(buf, 8) * 1e-3);
2711
17
    session->newdata.errEllipseMinor = (double)(getleu32(buf, 12) * 1e-3);
2712
17
    errEllipseOrient = getleu16(buf, 6);
2713
17
    errEllipseMajor = getleu32(buf, 8);
2714
17
    errEllipseMinor = getleu32(buf, 12);
2715
17
    GPSD_LOG(LOG_PROG, &session->context->errout,
2716
17
             "UBX: NAV-EELL: iTOW %lld version %u errEllipseOrient %u "
2717
17
             "errEllipseMajor %lu errEllipseMinor %lu\n",
2718
17
             (long long)session->driver.ubx.iTOW, version,
2719
17
             errEllipseOrient,
2720
17
             errEllipseMajor,
2721
17
             errEllipseMinor);
2722
17
    return 0;
2723
53
}
2724
2725
/**
2726
 * End of Epoch
2727
 * Not in u-blox 5, 6 or 7
2728
 * Present in:
2729
 *    protVer 18 (8-series, 9)
2730
 */
2731
static gps_mask_t ubx_msg_nav_eoe(struct gps_device_t *session,
2732
                                  unsigned char *buf, size_t data_len)
2733
24
{
2734
24
    if (4 > data_len) {
2735
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2736
1
                 "UBX: NAV-EOE: runt payload len %zd", data_len);
2737
1
        return 0;
2738
1
    }
2739
2740
23
    session->driver.ubx.iTOW = getleu32(buf, 0);
2741
23
    GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-EOE: iTOW=%lld\n",
2742
23
             (long long)session->driver.ubx.iTOW);
2743
    // nothing to report, but the iTOW for cycle ender is good
2744
23
    return 0;
2745
24
}
2746
2747
/*
2748
 * UBX-NAV-HPPOSECEF - High Precision Position Solution in ECEF
2749
 *
2750
 * Present in u-blox 8 and above, protVwer 20.00 and up.
2751
 * Only with High Precision firmware.
2752
 */
2753
static gps_mask_t ubx_msg_nav_hpposecef(struct gps_device_t *session,
2754
                                        unsigned char *buf,
2755
                                        size_t data_len UNUSED)
2756
40
{
2757
40
    gps_mask_t mask = ECEF_SET;
2758
2759
40
    unsigned version = getub(buf, 0);
2760
40
    if (0 != version) {
2761
21
        GPSD_LOG(LOG_WARN, &session->context->errout,
2762
21
                 "UBX: NAV-HPPOSECEF unknown version %u s/b 0", version);
2763
21
        return 0;
2764
21
    }
2765
19
    session->driver.ubx.iTOW = getleu32(buf, 4);
2766
19
    session->newdata.ecef.x = getles32x100s8d(buf, 8, 20, 1e-4);
2767
19
    session->newdata.ecef.y = getles32x100s8d(buf, 12, 21, 1e-4);
2768
19
    session->newdata.ecef.z = getles32x100s8d(buf, 16, 22, 1e-4);
2769
2770
19
    session->newdata.ecef.pAcc = getleu32(buf, 24) / (double)10000.0;
2771
    // (long long) cast for 32-bit compat
2772
19
    GPSD_LOG(LOG_PROG, &session->context->errout,
2773
19
        "UBX: NAV-HPPOSECEF: version %d iTOW %lld ecef X %.4f Y %.4f Z %.4f "
2774
19
        "pAcc %.4f\n",
2775
19
        version,
2776
19
        (long long)session->driver.ubx.iTOW,
2777
19
        session->newdata.ecef.x,
2778
19
        session->newdata.ecef.y,
2779
19
        session->newdata.ecef.z,
2780
19
        session->newdata.ecef.pAcc);
2781
19
    return mask;
2782
40
}
2783
2784
 /**
2785
 * High Precision Geodetic Position Solution
2786
 * UBX-NAV-HPPOSLLH, Class 1, ID x14
2787
 *
2788
 * No mode, so limited usefulness.
2789
 *
2790
 * Present in u-blox 8 and above, protVwer 20.00 and up.
2791
 * Only with High Precision firmware.
2792
 */
2793
static gps_mask_t ubx_msg_nav_hpposllh(struct gps_device_t *session,
2794
                                       unsigned char *buf, size_t data_len)
2795
42
{
2796
42
    int version;
2797
42
    gps_mask_t mask = 0;
2798
2799
42
    if (36 > data_len) {
2800
8
        GPSD_LOG(LOG_WARN, &session->context->errout,
2801
8
                 "UBX: NAV-HPPOSLLH: runt payload len %zd", data_len);
2802
8
        return mask;
2803
8
    }
2804
2805
34
    mask = ONLINE_SET | HERR_SET | VERR_SET | LATLON_SET | ALTITUDE_SET;
2806
2807
34
    version = getub(buf, 0);
2808
34
    session->driver.ubx.iTOW = getles32(buf, 4);
2809
34
    session->newdata.longitude = getles32x100s8d(buf, 8, 24, 1e-9);
2810
34
    session->newdata.latitude = getles32x100s8d(buf, 12, 25, 1e-9);
2811
    // altitude WGS84
2812
34
    session->newdata.altHAE = getles32x100s8d(buf, 16, 26, 1e-5);
2813
    // altitude MSL
2814
34
    session->newdata.altMSL = getles32x100s8d(buf, 20, 27, 1e-5);
2815
    // Let gpsd_error_model() deal with geoid_sep
2816
2817
    // Horizontal accuracy estimate in .1 mm, unknown est type
2818
34
    session->newdata.eph = getleu32(buf, 28) * (double)1e-4;
2819
    // Vertical accuracy estimate in .1 mm, unknown est type
2820
34
    session->newdata.epv = getleu32(buf, 32) * (double)1e-4;
2821
2822
34
    GPSD_LOG(LOG_PROG, &session->context->errout,
2823
34
        "UBX: NAV-HPPOSLLH: version %d iTOW=%lld lat=%.4f lon=%.4f "
2824
34
        "altHAE=%.4f\n",
2825
34
        version,
2826
34
        (long long)session->driver.ubx.iTOW,
2827
34
        session->newdata.latitude,
2828
34
        session->newdata.longitude,
2829
34
        session->newdata.altHAE);
2830
34
    return mask;
2831
42
}
2832
2833
/*
2834
 * Navigation Position ECEF message
2835
 *
2836
 * This message does not bother to tell us if it is valid.
2837
 */
2838
static gps_mask_t ubx_msg_nav_posecef(struct gps_device_t *session,
2839
                                      unsigned char *buf, size_t data_len)
2840
115
{
2841
115
    gps_mask_t mask = ECEF_SET;
2842
2843
115
    if (20 > data_len) {
2844
89
        GPSD_LOG(LOG_WARN, &session->context->errout,
2845
89
                 "UBX: NAV-POSECEF: runt payload len %zd", data_len);
2846
89
        return 0;
2847
89
    }
2848
2849
26
    session->driver.ubx.iTOW = getleu32(buf, 0);
2850
    // all in cm
2851
26
    session->newdata.ecef.x = getles32(buf, 4) * 1e-2;
2852
26
    session->newdata.ecef.y = getles32(buf, 8) * 1e-2;
2853
26
    session->newdata.ecef.z = getles32(buf, 12) * 1e-2;
2854
26
    session->newdata.ecef.pAcc = getleu32(buf, 16) * 1e-2;
2855
2856
    // (long long) cast for 32-bit compat
2857
26
    GPSD_LOG(LOG_PROG, &session->context->errout,
2858
26
        "UBX: NAV-POSECEF: iTOW=%lld ECEF x=%.2f y=%.2f z=%.2f pAcc=%.2f\n",
2859
26
        (long long)session->driver.ubx.iTOW,
2860
26
        session->newdata.ecef.x,
2861
26
        session->newdata.ecef.y,
2862
26
        session->newdata.ecef.z,
2863
26
        session->newdata.ecef.pAcc);
2864
26
    return mask;
2865
115
}
2866
2867
 /**
2868
 * Geodetic position solution message
2869
 * UBX-NAV-POSLLH, Class 1, ID 2
2870
 *
2871
 * This message does not bother to tell us if it is valid.
2872
 * No mode, so limited usefulness
2873
 */
2874
static gps_mask_t ubx_msg_nav_posllh(struct gps_device_t *session,
2875
                                     unsigned char *buf,
2876
                                     size_t data_len UNUSED)
2877
183
{
2878
183
    gps_mask_t mask = 0;
2879
2880
183
    if (28 > data_len) {
2881
114
        GPSD_LOG(LOG_WARN, &session->context->errout,
2882
114
                 "UBX: NAV-POSLLH: runt payload len %zd", data_len);
2883
114
        return 0;
2884
114
    }
2885
2886
69
    session->driver.ubx.iTOW = getleu32(buf, 0);
2887
69
    session->newdata.longitude = 1e-7 * getles32(buf, 4);
2888
69
    session->newdata.latitude = 1e-7 * getles32(buf, 8);
2889
    // altitude WGS84
2890
69
    session->newdata.altHAE = 1e-3 * getles32(buf, 12);
2891
    // altitude MSL
2892
69
    session->newdata.altMSL = 1e-3 * getles32(buf, 16);
2893
    // Let gpsd_error_model() deal with geoid_sep
2894
2895
    // Horizontal accuracy estimate in mm, unknown type
2896
69
    session->newdata.eph = getleu32(buf, 20) * 1e-3;
2897
    // Vertical accuracy estimate in mm, unknown type
2898
69
    session->newdata.epv = getleu32(buf, 24) * 1e-3;
2899
2900
69
    GPSD_LOG(LOG_PROG, &session->context->errout,
2901
69
        "UBX: NAV-POSLLH: iTOW=%lld lat=%.3f lon=%.3f altHAE=%.3f "
2902
69
        "eph %.3f epv %.3f\n",
2903
69
        (long long)session->driver.ubx.iTOW,
2904
69
        session->newdata.latitude,
2905
69
        session->newdata.longitude,
2906
69
        session->newdata.altHAE,
2907
69
        session->newdata.eph,
2908
69
        session->newdata.epv);
2909
2910
69
    mask = ONLINE_SET | HERR_SET | VERR_SET | LATLON_SET | ALTITUDE_SET;
2911
69
    return mask;
2912
183
}
2913
2914
/**
2915
 * Navigation Position Attitude Velocity Time solution message
2916
 * UBX-NAV-PVAT Class 1, ID 17
2917
 *
2918
 * Like UBX-NAV-PVT, plus parts of UBX-HNR-ATT, UBX-NAV-EELL, and NAV-TIMEUTC
2919
 *
2920
 * Present in:
2921
 *   protver 30  (ADR/DBD/HPS/LAP/MDR 9-series firmware)
2922
 *
2923
 * Not present in:
2924
 *    u-blox 5, 6, 7 or 8
2925
 */
2926
static gps_mask_t ubx_msg_nav_pvat(struct gps_device_t *session,
2927
                                  unsigned char *buf, size_t data_len UNUSED)
2928
214
{
2929
214
    char buf2[80];
2930
214
    char buf3[80];
2931
214
    char buf4[80];
2932
214
    unsigned version;
2933
214
    unsigned year, month, day, hour, min, sec;
2934
214
    double tAcc, hAcc, vAcc, sAcc;
2935
214
    double velN, velE, velD;
2936
214
    double vehRoll, vehPitch, vehHeading, motHeading;
2937
214
    double accRoll, accPitch, accHeading;
2938
214
    double magDec = NAN;
2939
214
    double magAcc = NAN;
2940
214
    long nano;
2941
214
    unsigned fixType;
2942
214
    unsigned flags;
2943
214
    unsigned flags2;
2944
214
    unsigned numSV;
2945
214
    unsigned valid;
2946
214
    int *status = &session->newdata.status;
2947
214
    int *mode = &session->newdata.mode;
2948
214
    gps_mask_t mask = 0;
2949
2950
214
    version = getub(buf, 4);
2951
214
    if (0 != version) {
2952
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
2953
1
                 "UBX: NAV-PVAT unknown version %u s/b 0", version);
2954
1
        return 0;
2955
1
    }
2956
213
    session->driver.ubx.iTOW = getleu32(buf, 0);
2957
213
    valid = getub(buf, 5);
2958
213
    year = getleu16(buf, 6);
2959
213
    month = getub(buf, 8);
2960
213
    day = getub(buf, 9);
2961
213
    hour = getub(buf, 10);
2962
213
    min = getub(buf, 11);
2963
213
    sec = getub(buf, 12);
2964
    // 13, 14, 15 reserved
2965
213
    tAcc = 1e-3 * getleu32(buf, 16);
2966
213
    nano = getles32(buf, 20);
2967
213
    fixType = getub(buf, 24);
2968
213
    flags = getub(buf, 25);
2969
213
    flags2 = getub(buf, 26);
2970
213
    numSV = getub(buf, 27);
2971
2972
    // u-blox doc admits this may differ from skyview data.
2973
213
    session->gpsdata.satellites_used = numSV;
2974
2975
213
    switch (fixType) {
2976
1
    case UBX_MODE_TMONLY:
2977
        // 5 - Surveyed-in, so a precise 3D.
2978
1
        *mode = MODE_3D;
2979
1
        *status = STATUS_TIME;
2980
1
        mask |= STATUS_SET | LATLON_SET | ALTITUDE_SET | MODE_SET | SPEED_SET;
2981
1
        break;
2982
2983
3
    case UBX_MODE_3D:
2984
        // 3
2985
3
        *mode = MODE_3D;
2986
3
        *status = STATUS_GPS;
2987
3
        mask |= STATUS_SET | LATLON_SET | ALTITUDE_SET | MODE_SET | SPEED_SET;
2988
3
        break;
2989
2990
2
    case UBX_MODE_GPSDR:
2991
        // 4
2992
2
        *mode = MODE_3D;
2993
2
        *status = STATUS_GNSSDR;
2994
2
        mask |= STATUS_SET | LATLON_SET | ALTITUDE_SET | MODE_SET | SPEED_SET;
2995
2
        break;
2996
2997
2
    case UBX_MODE_2D:
2998
        // 2
2999
2
        *mode = MODE_2D;
3000
2
        *status = STATUS_GPS;
3001
2
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
3002
2
        break;
3003
3004
6
    case UBX_MODE_DR:           // consider this too as 2D
3005
        // 1
3006
6
        *mode = MODE_2D;
3007
6
        *status = STATUS_DR;
3008
6
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
3009
6
        break;
3010
3011
33
    case UBX_MODE_NOFIX:
3012
        // 0
3013
33
        FALLTHROUGH
3014
199
    default:
3015
        // huh?
3016
199
        *mode = MODE_NO_FIX;
3017
199
        *status = STATUS_UNK;
3018
199
        mask |= MODE_SET | STATUS_SET;
3019
199
        break;
3020
213
    }
3021
3022
213
    if (UBX_NAV_PVT_FLAG_DGPS == (flags & UBX_NAV_PVT_FLAG_DGPS)) {
3023
110
        if (STATUS_TIME == *status) {
3024
            // leave it
3025
109
        } else if (UBX_NAV_PVT_FLAG_RTK_FIX ==
3026
109
                   (flags & UBX_NAV_PVT_FLAG_RTK_FIX)) {
3027
68
            *status = STATUS_RTK_FIX;
3028
68
        } else if (UBX_NAV_PVT_FLAG_RTK_FLT ==
3029
41
                   (flags & UBX_NAV_PVT_FLAG_RTK_FLT)) {
3030
18
            *status = STATUS_RTK_FLT;
3031
23
        } else {
3032
23
            *status = STATUS_DGPS;
3033
23
        }
3034
110
        mask |= STATUS_SET;
3035
110
    }
3036
3037
213
    if ((valid & UBX_NAV_PVT_VALID_DATE_TIME) == UBX_NAV_PVT_VALID_DATE_TIME) {
3038
211
        struct tm unpacked_date = {0};
3039
3040
211
        unpacked_date.tm_year = year - 1900;
3041
211
        unpacked_date.tm_mon = month - 1;
3042
211
        unpacked_date.tm_mday = day;
3043
211
        unpacked_date.tm_hour = hour;
3044
211
        unpacked_date.tm_min = min;
3045
211
        unpacked_date.tm_sec = sec;
3046
211
        session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
3047
        // nano, can be negative! So normalize
3048
211
        session->newdata.time.tv_nsec = nano;
3049
211
        TS_NORM(&session->newdata.time);
3050
        // compute GPS week
3051
211
        fix_week(session);
3052
3053
211
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
3054
211
    }
3055
3056
213
    if (LATLON_SET == (mask & LATLON_SET)) {
3057
14
        session->newdata.longitude = 1e-7 * getles32(buf, 28);
3058
14
        session->newdata.latitude = 1e-7 * getles32(buf, 32);
3059
14
        if (ALTITUDE_SET == (mask & ALTITUDE_SET)) {
3060
            // altitude WGS84
3061
6
            session->newdata.altHAE = 1e-3 * getles32(buf, 36);
3062
            // altitude MSL
3063
6
            session->newdata.altMSL = 1e-3 * getles32(buf, 40);
3064
            // Let gpsd_error_model() deal with geoid_sep
3065
6
        }
3066
14
    }
3067
213
    hAcc = 1e-3 * getleu32(buf, 44);
3068
213
    vAcc = 1e-3 * getleu32(buf, 48);
3069
213
    velN = 1e-3 * getles32(buf, 52);
3070
213
    velE = 1e-3 * getles32(buf, 56);
3071
213
    velD = 1e-3 * getles32(buf, 60);
3072
3073
    // Seems to be always valid.
3074
213
    session->newdata.eph = hAcc;
3075
213
    session->newdata.epv = vAcc;
3076
213
    session->newdata.NED.velN = velN;
3077
213
    session->newdata.NED.velE = velE;
3078
213
    session->newdata.NED.velD = velD;
3079
213
    mask |= VNED_SET;
3080
3081
    // gSpeed, seems to be always valid.
3082
213
    session->newdata.speed = 1e-3 * getles32(buf, 64);
3083
213
    sAcc = 1e-3 * getleu32(buf, 68);
3084
213
    session->newdata.eps = sAcc;
3085
213
    mask |= SPEED_SET;
3086
3087
213
    vehRoll = 1e-5 * getles32(buf, 72);
3088
213
    vehPitch = 1e-5 * getles32(buf, 76);
3089
213
    vehHeading = 1e-5 * getles32(buf, 80);
3090
3091
    // accuracies
3092
213
    accRoll = 1e-3 * getles32(buf, 88);
3093
213
    accPitch = 1e-3 * getles32(buf, 90);
3094
213
    accHeading = 1e-3 * getles32(buf, 92);
3095
3096
213
    if (0.0 != accRoll) {
3097
196
        session->gpsdata.attitude.roll = vehRoll;
3098
196
        mask |= ATTITUDE_SET;
3099
196
    }
3100
213
    if (0.0 != accPitch) {
3101
195
        session->gpsdata.attitude.pitch = vehPitch;
3102
195
        mask |= ATTITUDE_SET;
3103
195
    }
3104
213
    if (0.0 != accHeading) {
3105
        // seems to be true heading
3106
202
        session->gpsdata.attitude.heading = vehHeading;
3107
202
        mask |= ATTITUDE_SET;
3108
202
    }
3109
3110
213
    motHeading = 1e-5 * getles32(buf, 84);
3111
213
    if (flags & UBX_NAV_PVT_FLAG_HDG_OK) {
3112
        // u-blox calls this Heading of motion (2-D)
3113
110
        session->newdata.track = motHeading;
3114
110
        mask |= TRACK_SET;
3115
110
    }
3116
3117
213
    if (ATTITUDE_SET == (mask & ATTITUDE_SET)) {
3118
207
        session->gpsdata.attitude.mtime = session->newdata.time;
3119
207
    }
3120
3121
213
    if (valid & UBX_NAV_PVT_VALID_MAG) {
3122
93
        magDec = (double)(getles16(buf, 94) * 1e-2);
3123
93
        magAcc = (double)(getleu16(buf, 96) * 1e-2);
3124
93
    }
3125
213
    session->newdata.errEllipseOrient = (double)(getleu16(buf, 98) * 1e-2);
3126
213
    session->newdata.errEllipseMajor = (double)(getleu32(buf, 100) * 1e-3);
3127
213
    session->newdata.errEllipseMinor = (double)(getleu32(buf, 104) * 1e-3);
3128
3129
    // if cycle ender worked, could get rid of this REPORT_IS.
3130
    // mask |= REPORT_IS;
3131
3132
213
    GPSD_LOG(LOG_PROG, &session->context->errout,
3133
213
         "UBX: NAV-PVAT: iTOW %lld version %d valid x%02x "
3134
213
         "time %u/%02u/%02u %02u:%02u:%02u tAcc %.3f nano %lu fixType %u "
3135
213
         "flags x%x flags2 x%x numSV %u lat %.2f lon %.2f "
3136
213
         "altHAE %.2f altMSL %.2f "
3137
213
         "hAcc %.3f vAcc %.3f valNED %.3f %.3f %.3f speed %.3f sAcc %.3f "
3138
213
         "vehRPH %.5f %.5f %.5f "
3139
213
         "track %.2f accRPH %.3f %.3f %.3f "
3140
213
         "mode %d status %d used %d magDec %.2f magAcc %.2f "
3141
213
         "errEllipse %.2f %.3f %.3f\n",
3142
213
         (long long)session->driver.ubx.iTOW, version,
3143
         // timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
3144
213
         valid, year, month, day, hour, min, sec, tAcc, nano, fixType,
3145
213
         flags, flags2, numSV,
3146
213
         session->newdata.latitude,
3147
213
         session->newdata.longitude,
3148
213
         session->newdata.altHAE,
3149
213
         session->newdata.altMSL,
3150
213
         hAcc, vAcc, velN, velE, velD, session->newdata.speed, sAcc,
3151
213
         vehRoll, vehPitch, vehHeading,
3152
213
         motHeading,
3153
         //session->newdata.track,
3154
213
         accRoll, accPitch, accHeading,
3155
213
         session->newdata.mode,
3156
213
         session->newdata.status,
3157
213
         session->gpsdata.satellites_used,
3158
213
         magDec, magAcc,
3159
213
         session->newdata.errEllipseOrient,
3160
213
         session->newdata.errEllipseMajor,
3161
213
         session->newdata.errEllipseMinor);
3162
213
    GPSD_LOG(LOG_IO, &session->context->errout,
3163
213
             "UBX: NAV-PVAT: fixType(%s) flags(%s) flags2(%s) valid(%s)\n",
3164
213
             val2str(fixType, vpvt_fixType),
3165
213
             flags2str(flags, fnav_pvt_flags, buf2, sizeof(buf2)),
3166
213
             flags2str(flags2, fpvt_flags2, buf3, sizeof(buf3)),
3167
213
             flags2str(valid, fpvt_valid, buf4, sizeof(buf4)));
3168
213
    return mask;
3169
213
}
3170
3171
/**
3172
 * Navigation Position Velocity Time solution message
3173
 * UBX-NAV-PVT Class 1, ID 7
3174
 *
3175
 * Includes part of UBX-NAV-TIMEUTC
3176
 *
3177
 * Present in:
3178
 *   protver 14  (6-series w/ GLONASS, 7-series)
3179
 *
3180
 * Not present in:
3181
 *    u-blox 5 or 6
3182
 */
3183
static gps_mask_t ubx_msg_nav_pvt(struct gps_device_t *session,
3184
                                  unsigned char *buf, size_t data_len UNUSED)
3185
540
{
3186
540
    char buf2[80];
3187
540
    char buf3[80];
3188
540
    char buf4[80];
3189
540
    char buf5[80];
3190
540
    unsigned dgps_age;
3191
540
    struct tm unpacked_date = {0};
3192
540
    int *status = &session->newdata.status;
3193
540
    int *mode = &session->newdata.mode;
3194
540
    gps_mask_t mask = 0;
3195
540
    char ts_buf[TIMESPEC_LEN];
3196
3197
540
    session->driver.ubx.iTOW = getleu32(buf, 0);
3198
540
    unsigned valid = getub(buf, 11);
3199
540
    unsigned fixType = getub(buf, 20);
3200
540
    unsigned flags = getub(buf, 21);
3201
540
    unsigned flags2 = getub(buf, 22);
3202
540
    unsigned numSV = getub(buf, 23);
3203
540
    unsigned flags3 = getleu16(buf, 78);
3204
3205
    // session->gpsdata.satellites_used = numSV;
3206
3207
540
    switch (fixType) {
3208
14
    case UBX_MODE_TMONLY:
3209
        // 5 - Surveyed-in, so a precise 3D.
3210
14
        *mode = MODE_3D;
3211
14
        *status = STATUS_TIME;
3212
14
        mask |= STATUS_SET | MODE_SET;
3213
14
        break;
3214
3215
70
    case UBX_MODE_3D:
3216
        // 3
3217
70
        *mode = MODE_3D;
3218
70
        *status = STATUS_GPS;
3219
70
        mask |= STATUS_SET | LATLON_SET | MODE_SET;
3220
70
        break;
3221
3222
9
    case UBX_MODE_GPSDR:
3223
        // 4
3224
9
        *mode = MODE_3D;
3225
9
        *status = STATUS_GNSSDR;
3226
9
        mask |= STATUS_SET | LATLON_SET | MODE_SET;
3227
9
        break;
3228
3229
1
    case UBX_MODE_2D:
3230
        // 2
3231
1
        *mode = MODE_2D;
3232
1
        *status = STATUS_GPS;
3233
1
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
3234
1
        break;
3235
3236
6
    case UBX_MODE_DR:           // consider this too as 2D
3237
        // 1
3238
6
        *mode = MODE_2D;
3239
6
        *status = STATUS_DR;
3240
6
        mask |= LATLON_SET | SPEED_SET | MODE_SET | STATUS_SET;
3241
6
        break;
3242
3243
118
    case UBX_MODE_NOFIX:
3244
        // 0
3245
118
        FALLTHROUGH
3246
440
    default:
3247
        // huh?
3248
440
        *mode = MODE_NO_FIX;
3249
440
        *status = STATUS_UNK;
3250
440
        mask |= MODE_SET | STATUS_SET;
3251
440
        break;
3252
540
    }
3253
3254
540
    if (UBX_NAV_PVT_FLAG_DGPS == (flags & UBX_NAV_PVT_FLAG_DGPS)) {
3255
        // RTK flags not before protoVer 20.
3256
277
        if (STATUS_TIME == *status) {
3257
            // leave it
3258
264
        } else if (UBX_NAV_PVT_FLAG_RTK_FIX ==
3259
264
                   (flags & UBX_NAV_PVT_FLAG_RTK_FIX)) {
3260
110
            *status = STATUS_RTK_FIX;
3261
154
        } else if (UBX_NAV_PVT_FLAG_RTK_FLT ==
3262
154
                   (flags & UBX_NAV_PVT_FLAG_RTK_FLT)) {
3263
67
            *status = STATUS_RTK_FLT;
3264
87
        } else {
3265
87
            *status = STATUS_DGPS;
3266
87
        }
3267
3268
277
        dgps_age = (flags3 >> 1) & 0x0f;
3269
277
        if (0 < dgps_age) {
3270
191
            if (ROWS(pvt_dgps_age) <= dgps_age) {
3271
61
                dgps_age = ROWS(pvt_dgps_age) - 1;
3272
61
            }
3273
191
            session->newdata.dgps_age = pvt_dgps_age[dgps_age];
3274
191
        }
3275
277
        mask |= STATUS_SET;
3276
277
    }
3277
3278
540
    if ((valid & UBX_NAV_PVT_VALID_DATE_TIME) == UBX_NAV_PVT_VALID_DATE_TIME) {
3279
388
        unpacked_date.tm_year = (uint16_t)getleu16(buf, 4) - 1900;
3280
388
        unpacked_date.tm_mon = (uint8_t)getub(buf, 6) - 1;
3281
388
        unpacked_date.tm_mday = (uint8_t)getub(buf, 7);
3282
388
        unpacked_date.tm_hour = (uint8_t)getub(buf, 8);
3283
388
        unpacked_date.tm_min = (uint8_t)getub(buf, 9);
3284
388
        unpacked_date.tm_sec = (uint8_t)getub(buf, 10);
3285
388
        unpacked_date.tm_isdst = 0;
3286
388
        unpacked_date.tm_wday = 0;
3287
388
        unpacked_date.tm_yday = 0;
3288
388
        session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
3289
        // field 16, nano, can be negative! So normalize
3290
388
        session->newdata.time.tv_nsec = getles32(buf, 16);
3291
388
        TS_NORM(&session->newdata.time);
3292
        // compute GPS week
3293
388
        fix_week(session);
3294
3295
388
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
3296
388
    }
3297
3298
540
    session->newdata.longitude = 1e-7 * getles32(buf, 24);
3299
540
    session->newdata.latitude = 1e-7 * getles32(buf, 28);
3300
    // altitude WGS84
3301
540
    session->newdata.altHAE = 1e-3 * getles32(buf, 32);
3302
    // altitude MSL
3303
540
    session->newdata.altMSL = 1e-3 * getles32(buf, 36);
3304
    // Let gpsd_error_model() deal with geoid_sep
3305
3306
540
    session->newdata.speed = 1e-3 * (int32_t)getles32(buf, 60);
3307
    // u-blox calls this Heading of motion (2-D)
3308
540
    session->newdata.track = 1e-5 * (int32_t)getles32(buf, 64);
3309
    // FIXME!!!!!
3310
540
    mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET;
3311
3312
    /* u-blox does not document the basis for the following "accuracy"
3313
     * estimates.  Maybe CEP(50), one sigma, two sigma, CEP(99), etc. */
3314
3315
    // Horizontal Accuracy estimate, in mm
3316
540
    session->newdata.eph = (double)(getles32(buf, 40) / 1000.0);
3317
    // Vertical Accuracy estimate, in mm
3318
540
    session->newdata.epv = (double)(getles32(buf, 44) / 1000.0);
3319
    // Speed Accuracy estimate, in mm/s
3320
540
    session->newdata.eps = (double)(getles32(buf, 68) / 1000.0);
3321
    // let gpsd_error_model() do the rest
3322
3323
540
    mask |= HERR_SET | SPEEDERR_SET | VERR_SET;
3324
    // if cycle ender worked, could get rid of this REPORT_IS.
3325
    // mask |= REPORT_IS;
3326
3327
540
    GPSD_LOG(LOG_PROG, &session->context->errout,
3328
540
         "UBX: NAV-PVT: flags %02x time %s valid x%x lat %.2f lon %.2f "
3329
540
         "altHAE %.2f "
3330
540
         "track %.2f speed %.2f mode %d status %d used %d dgps_age %.0f\n",
3331
540
         flags,
3332
540
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
3333
540
         valid,
3334
540
         session->newdata.latitude,
3335
540
         session->newdata.longitude,
3336
540
         session->newdata.altHAE,
3337
540
         session->newdata.track,
3338
540
         session->newdata.speed,
3339
540
         session->newdata.mode,
3340
540
         session->newdata.status,
3341
540
         numSV,   // session->gpsdata.satellites_used,
3342
540
         session->newdata.dgps_age);
3343
540
    GPSD_LOG(LOG_IO, &session->context->errout,
3344
540
             "UBX: NAV-PVT: fixType %s flags(%s_ flags2(%s) flags3(%s) "
3345
540
             "valid(%s)\n",
3346
540
             val2str(fixType, vpvt_fixType),
3347
540
             flags2str(flags, fnav_pvt_flags, buf2, sizeof(buf2)),
3348
540
             flags2str(flags2, fpvt_flags2, buf3, sizeof(buf3)),
3349
540
             flags2str(flags3, fpvt_flags3, buf4, sizeof(buf4)),
3350
540
             flags2str(valid, fpvt_valid, buf5, sizeof(buf5)));
3351
540
    if (92 <= data_len) {
3352
        // u-blox 8 and 9 extended
3353
379
        double magDec = NAN;
3354
379
        double magAcc = NAN;
3355
#ifdef __UNUSED
3356
        if (flags & UBX_NAV_PVT_FLAG_HDG_OK) {
3357
            /* u-blox calls this Heading of vehicle (2-D)
3358
             * why is it different than earlier track? */
3359
            session->newdata.track = (double)(getles32(buf, 84) * 1e-5);
3360
        }
3361
#endif  // __UNUSED
3362
379
        if (valid & UBX_NAV_PVT_VALID_MAG) {
3363
203
            magDec = (double)(getles16(buf, 88) * 1e-2);
3364
203
            magAcc = (double)(getleu16(buf, 90) * 1e-2);
3365
203
        }
3366
379
        GPSD_LOG(LOG_PROG, &session->context->errout,
3367
379
             " UBX: NAV-PVT: headVeh %.5f magDec %.2f magAcc %.2f\n",
3368
379
             session->newdata.track, magDec, magAcc);
3369
379
    }
3370
540
    return mask;
3371
540
}
3372
3373
 /**
3374
 * High Precision Relative Positioning Information in NED frame
3375
 * UBX-NAV-RELPOSNED, Class 1, ID x3c
3376
 * HP GNSS only, protver 20+
3377
 */
3378
static gps_mask_t ubx_msg_nav_relposned(struct gps_device_t *session,
3379
                                        unsigned char *buf, size_t data_len)
3380
134
{
3381
134
    int version;
3382
134
    unsigned flags;
3383
134
    double accN = NAN, accE = NAN, accD = NAN, accL = NAN, accH = NAN;
3384
134
    gps_mask_t mask = 0;
3385
3386
134
    if (40 > data_len) {
3387
4
        GPSD_LOG(LOG_WARN, &session->context->errout,
3388
4
                 "UBX: NAV-RELPOSNED:0: runt payload len %zd",
3389
4
                 data_len);
3390
4
        return mask;
3391
4
    }
3392
130
    version = getub(buf, 0);
3393
    /* WTF?  u-blox did not make this sentence upward compatible
3394
     * 40 bytes in Version 0, protVer 20 to 27
3395
     * 64 bytes in Version 1, protVer 27.11+ */
3396
3397
130
    session->newdata.dgps_station = getleu16(buf, 2);          // 0 to 4095
3398
130
    session->driver.ubx.iTOW = getleu32(buf, 4);
3399
130
    if (1 > version) {
3400
        // version 0
3401
3
        flags = getleu32(buf, 36);
3402
3
        if (1 != (1 & flags)) {
3403
            // not gnssFixOK
3404
1
            GPSD_LOG(LOG_PROG, &session->context->errout,
3405
1
                     "UBX: NAV-RELPOSNED:0 no fix");
3406
1
            return mask;
3407
1
        }
3408
2
        if (4 & flags) {
3409
            // rePosValid
3410
1
            session->newdata.NED.relPosN = getles32x100s8d(buf, 8, 20, 1e-4);
3411
1
            session->newdata.NED.relPosE = getles32x100s8d(buf, 12, 21, 1e-4);
3412
1
            session->newdata.NED.relPosD = getles32x100s8d(buf, 16, 22, 1e-4);
3413
3414
1
            accN = 1e-4 * getles32(buf, 24);
3415
1
            accE = 1e-4 * getles32(buf, 28);
3416
1
            accD = 1e-4 * getles32(buf, 32);
3417
1
            mask |= NED_SET;
3418
1
        }
3419
127
    } else {
3420
        // assume version 1
3421
127
        if (64 > data_len) {
3422
5
            GPSD_LOG(LOG_WARN, &session->context->errout,
3423
5
                     "UBX: NAV-RELPOSNED:1: runt payload len %zd",
3424
5
                     data_len);
3425
5
            return mask;
3426
5
        }
3427
122
        flags = getleu32(buf, 60);
3428
122
        if (1 != (1 & flags)) {
3429
            // not gnssFixOK
3430
35
            GPSD_LOG(LOG_PROG, &session->context->errout,
3431
35
                     "UBX: NAV-RELPOSNED:1 no fix");
3432
35
            return mask;
3433
35
        }
3434
87
        if (4 & flags) {
3435
            // rePosValid
3436
56
            session->newdata.NED.relPosN = getles32x100s8d(buf, 8, 32, 1e-4);
3437
56
            session->newdata.NED.relPosE = getles32x100s8d(buf, 12, 33, 1e-4);
3438
56
            session->newdata.NED.relPosD = getles32x100s8d(buf, 16, 34, 1e-4);
3439
56
            session->newdata.NED.relPosL = getles32x100s8d(buf, 20, 35, 1e-4);
3440
3441
56
            accN = 1e-4 * getles32(buf, 36);
3442
56
            accE = 1e-4 * getles32(buf, 40);
3443
56
            accD = 1e-4 * getles32(buf, 44);
3444
56
            accL = 1e-4 * getles32(buf, 48);
3445
56
            accH = 1e-4 * getles32(buf, 52);
3446
56
            if (0x100 & flags) {
3447
                // relPosHeadingValid
3448
20
                session->newdata.NED.relPosH = 1e-5 * getles32(buf, 24);
3449
20
            }
3450
56
            mask |= NED_SET;
3451
            // FIXME: RTK flags?
3452
56
        }
3453
87
    }
3454
3455
89
    GPSD_LOG(LOG_PROG, &session->context->errout,
3456
89
        "UBX: NAV-RELPOSNED: version %d iTOW=%lld refStationId %u flags x%x\n"
3457
89
        "UBX: NAV-RELPOSNED: relPos N=%.4f E=%.4f D=%.4f\n"
3458
89
        "UBX: NAV-RELPOSNED: acc N=%.4f E=%.4f D=%.4f L=%.4f H=%.4f\n",
3459
89
        version,
3460
89
        (long long)session->driver.ubx.iTOW,
3461
89
        session->newdata.dgps_station,
3462
89
        flags,
3463
89
        session->newdata.NED.relPosN,
3464
89
        session->newdata.NED.relPosE,
3465
89
        session->newdata.NED.relPosD,
3466
89
        accN, accE, accD, accL, accH);
3467
3468
89
    if (5 != (flags & 5)) {
3469
        // gnssFixOK or relPosValid are false, no fix
3470
32
        return 0;
3471
32
    }
3472
57
    return mask;
3473
89
}
3474
3475
/**
3476
 * GPS Satellite Info -- new style UBX-NAV-SAT
3477
 * Not present in:
3478
 *     u-blox 5
3479
 *     protVer 12  5 and 6-series
3480
 * Present in:
3481
 *    protVer 15_  8-series
3482
 *    protVer 27   (ZED-F9P)
3483
 */
3484
static gps_mask_t ubx_msg_nav_sat(struct gps_device_t *session,
3485
                                  unsigned char *buf, size_t data_len)
3486
501
{
3487
501
    char buf2[80];
3488
501
    unsigned i, nchan, ver;
3489
501
    unsigned seen = 0, used_tot = 0;
3490
501
    timespec_t ts_tow;
3491
501
    char ts_buf[TIMESPEC_LEN];
3492
3493
501
    if (8 > data_len) {
3494
262
        GPSD_LOG(LOG_PROG, &session->context->errout,
3495
262
                 "UBX: NAV-SAT runt datalen %zd\n", data_len);
3496
262
        return 0;
3497
262
    }
3498
3499
239
    session->driver.ubx.iTOW = getleu32(buf, 0);
3500
239
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
3501
239
    session->gpsdata.skyview_time =
3502
239
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
3503
3504
239
    ver = getub(buf, 4);
3505
239
    if (1 != ver) {
3506
91
        GPSD_LOG(LOG_WARN, &session->context->errout,
3507
91
                 "UBX: NAV-SAT unknown version %d\n", ver);
3508
91
        return 0;
3509
91
    }
3510
148
    nchan = getub(buf, 5);
3511
148
    if (nchan > MAXCHANNELS) {
3512
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
3513
2
                 "UBX: NAV-SAT: too many channels %d\n",
3514
2
                 nchan);
3515
2
        return 0;
3516
2
    }
3517
146
    if ((8 + 12 * nchan) > data_len) {
3518
8
        GPSD_LOG(LOG_WARN, &session->context->errout,
3519
8
                 "UBX: NAV-SAT: runt payload %zd channels %u\n",
3520
8
                 data_len, nchan);
3521
8
        return 0;
3522
8
    }
3523
3524
#ifdef __UNUSED__    // debug
3525
    GPSD_LOG(LOG_SHOUT, &session->context->errout,
3526
             "UBX: NAV-SAT: gpsd_zero_satellites()\n");
3527
#endif
3528
138
    gpsd_zero_satellites(&session->gpsdata);
3529
3530
6.83k
    for (i = 0; i < nchan; i++) {
3531
6.69k
        unsigned off = 8 + 12 * i;
3532
6.69k
        short nmea_PRN = 0;
3533
6.69k
        uint8_t gnssId = getub(buf, off + 0);
3534
6.69k
        uint8_t svId = getub(buf, off + 1);
3535
6.69k
        uint8_t cno = getub(buf, off + 2);
3536
6.69k
        int elev = getsb(buf, off + 3);
3537
6.69k
        int azim = getles16(buf, off + 4);
3538
6.69k
        int prRes = getles16(buf, off + 6);
3539
        // health data in flags
3540
6.69k
        unsigned long flags = getleu32(buf, off + 8);
3541
6.69k
        bool used = (bool)(flags  & 0x08);
3542
        // Notice NO sigid!
3543
3544
6.69k
        nmea_PRN = ubx2_to_prn(gnssId, svId);
3545
6.69k
        if (0 >= nmea_PRN) {
3546
            // bad PRN??
3547
5.09k
            GPSD_LOG(LOG_WARN, &session->context->errout,
3548
5.09k
                     "UBX: NAV-SAT(%d) Bad PRN: gnssid %u, svid %u PRN %d\n",
3549
5.09k
                     seen, gnssId, svId, nmea_PRN);
3550
5.09k
            continue;
3551
5.09k
        }
3552
1.59k
        session->gpsdata.skyview[seen].gnssid = gnssId;
3553
1.59k
        session->gpsdata.skyview[seen].svid = svId;
3554
1.59k
        session->gpsdata.skyview[seen].PRN = nmea_PRN;
3555
3556
1.59k
        session->gpsdata.skyview[seen].ss = (double)cno;
3557
1.59k
        if (90 >= abs(elev)) {
3558
1.39k
            session->gpsdata.skyview[seen].elevation = (double)elev;
3559
1.39k
        }
3560
        /* For some reason UBX allows 360 == azim here, but gpsd json does not
3561
         * so fix that.  Other UBX specifies 0-359. */
3562
1.59k
        if (360 == azim) {
3563
11
            azim = 0;
3564
11
        }
3565
1.59k
        if (360 > azim &&
3566
797
            0 <= azim) {
3567
477
            session->gpsdata.skyview[seen].azimuth = (double)azim;
3568
477
        }
3569
1.59k
        session->gpsdata.skyview[seen].used = used;
3570
        // sbas_in_use is not same as used
3571
1.59k
        if (used) {
3572
525
            used_tot++;
3573
525
        }
3574
1.59k
        session->gpsdata.skyview[seen].prRes = prRes / 10.0;
3575
        // by some coincidence, our health flags matches u-blox's
3576
1.59k
        session->gpsdata.skyview[seen].health = (flags >> 4) & 3;
3577
1.59k
        session->gpsdata.skyview[seen].qualityInd = flags & 7;
3578
        // FIXME: sigid?
3579
1.59k
        GPSD_LOG(LOG_PROG, &session->context->errout,
3580
1.59k
                 "UBX: NAV-SAT(%d) gnssid %u, svid %u PRN %d "
3581
1.59k
                 "prRes %d cno %u el %.1f az %.1f qual %d flags x%lx\n",
3582
1.59k
                 seen, gnssId, svId, nmea_PRN, prRes, cno,
3583
1.59k
                 session->gpsdata.skyview[seen].elevation,
3584
1.59k
                 session->gpsdata.skyview[seen].azimuth,
3585
1.59k
                 session->gpsdata.skyview[seen].qualityInd,
3586
1.59k
                 flags);
3587
1.59k
        GPSD_LOG(LOG_IO, &session->context->errout,
3588
1.59k
                 "UBX: NAV-SAT: gnssId:%s flags:%s quality:%s\n",
3589
1.59k
                 val2str(gnssId, vgnssId),
3590
1.59k
                 flags2str(flags, fsat_flags, buf2, sizeof(buf2)),
3591
1.59k
                 val2str(flags & 7, vquality));
3592
3593
1.59k
        seen++;
3594
1.59k
    }
3595
3596
138
    session->gpsdata.satellites_visible = seen;
3597
138
    session->gpsdata.satellites_used = used_tot;
3598
138
    GPSD_LOG(LOG_PROG, &session->context->errout,
3599
138
             "UBX: NAV-SAT: time %s visible %d used %d "
3600
138
             "mask={SATELLITE|USED}\n",
3601
138
             timespec_str(&session->gpsdata.skyview_time,
3602
138
                          ts_buf, sizeof(ts_buf)),
3603
138
             session->gpsdata.satellites_visible,
3604
138
             session->gpsdata.satellites_used);
3605
138
    return SATELLITE_SET | USED_IS;
3606
146
}
3607
3608
/*
3609
 * SBAS Info UBX-NAV-SBAS
3610
 * in u-blox 4_
3611
 * in NEO-M9N
3612
 * Not in some u-blox 9
3613
 * Decode looks good, but data only goes to log.
3614
 */
3615
static gps_mask_t ubx_msg_nav_sbas(struct gps_device_t *session,
3616
                                   unsigned char *buf, size_t data_len)
3617
944
{
3618
944
    unsigned i, cnt;
3619
944
    unsigned ubx_PRN;
3620
944
    short nmea_PRN;
3621
944
    gnssid_t gnssid = 0;
3622
944
    unsigned char svid = 0;
3623
3624
944
    if (12 > data_len) {
3625
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
3626
2
                 "UBX: NAV-SBAS: runt payload len %zd", data_len);
3627
2
        return 0;
3628
2
    }
3629
3630
942
    session->driver.ubx.iTOW = getleu32(buf, 0);
3631
942
    ubx_PRN = getub(buf, 4);
3632
942
    cnt = getub(buf, 8);
3633
942
    GPSD_LOG(LOG_PROG, &session->context->errout,
3634
942
             "UBX: NAV-SBAS iTOW %lu geo %u mode %u sys %u service x%x "
3635
942
             "cnt %u\n",
3636
942
             (unsigned long)session->driver.ubx.iTOW,
3637
942
             ubx_PRN, (unsigned)getub(buf, 5),
3638
942
             (unsigned)getub(buf, 6), (unsigned)getub(buf, 7),
3639
942
             cnt);
3640
3641
942
    if (MAXCHANNELS < cnt) {
3642
        // too many sats for us, pacify coverity
3643
83
        cnt = MAXCHANNELS;
3644
83
    }
3645
942
    if (data_len < (12 + (12 * cnt))) {
3646
        // length check, pacify coverity
3647
137
        GPSD_LOG(LOG_WARN, &session->context->errout,
3648
137
                 "UBX: NAV-SBAS: bad length %zd", data_len);
3649
137
        return 0;
3650
137
    }
3651
1.87k
    for (i = 0; i < cnt; i++) {
3652
1.07k
        unsigned off = 12 + (12 * i);
3653
1.07k
        unsigned svID = getub(buf, off);
3654
1.07k
        unsigned flags = getub(buf, off + 1);
3655
        // User Differential Range Error (udre)
3656
1.07k
        unsigned udre = getub(buf, off + 2);
3657
1.07k
        int svSys = getsb(buf, off + 3);
3658
1.07k
        unsigned svService = getub(buf, off + 4);
3659
1.07k
        int prc = getles16(buf, off + 6);
3660
1.07k
        int ic = getles16(buf, off + 10);
3661
1.07k
        GPSD_LOG(LOG_PROG, &session->context->errout,
3662
1.07k
                 "UBX: NAV-SBAS SV%3u flags x%02x udre %u svSys %2d "
3663
1.07k
                 "svService x%x prc %d ic %d\n",
3664
1.07k
                 svID, flags, udre, svSys, svService, prc, ic);
3665
1.07k
    }
3666
    /* really 'in_use' depends on the sats info, EGNOS is still
3667
     * in test.  In WAAS areas one might also check for the type of
3668
     * corrections indicated
3669
     */
3670
3671
805
    nmea_PRN = ubx_to_prn(ubx_PRN, &gnssid, &svid);
3672
#ifdef __UNUSED
3673
    // debug
3674
    GPSD_LOG(LOG_ERROR, &session->context->errout,
3675
             "UBX: NAV-SBAS ubx_prn %d gnssid %d, svid %d nmea_PRN %d\n",
3676
             ubx_PRN, gnssid, svid, nmea_PRN);
3677
#endif  // __UNUSED
3678
805
    session->driver.ubx.sbas_in_use = nmea_PRN;
3679
805
    return 0;
3680
942
}
3681
3682
/**
3683
 * Satellite Info -- UBX-NAV-SIG
3684
 *
3685
 * Like NAV-SAT, but NAV-SIG has no elevation and azimuth!  So we need both.
3686
 * Assume NAV-SAT was sent in this epoch before NAV-SIG.
3687
 * Seems like NAV-SAT always sent just before NAV-SIG.
3688
 *
3689
 * Present in:
3690
 *    protVer 27 (9-series and 10)
3691
 * Not present in:
3692
 *    protVer 12 6-eries
3693
 *    before protVer 27
3694
 */
3695
static gps_mask_t ubx_msg_nav_sig(struct gps_device_t *session,
3696
                                  unsigned char *buf, size_t data_len)
3697
513
{
3698
513
    unsigned int i, nchan, ver;
3699
513
    int seen = 0, used_tot = 0;
3700
513
    timespec_t ts_tow;
3701
    // saved skyview, hopefully from NAV-SAT
3702
513
    struct satellite_t skyview_old[MAXCHANNELS];
3703
513
    char ts_buf[TIMESPEC_LEN];
3704
3705
513
    if (8 > data_len) {
3706
2
        GPSD_LOG(LOG_PROG, &session->context->errout,
3707
2
                 "UBX: NAV-SIG runt datalen %zd\n", data_len);
3708
2
        return 0;
3709
2
    }
3710
3711
511
    session->driver.ubx.iTOW = getleu32(buf, 0);
3712
511
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
3713
511
    session->gpsdata.skyview_time =
3714
511
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
3715
3716
511
    ver = getub(buf, 4);
3717
511
    if (0 != ver) {
3718
78
        GPSD_LOG(LOG_WARN, &session->context->errout,
3719
78
                 "UBX: NAV-SIG unknown version %d s/b 0", ver);
3720
78
        return 0;
3721
78
    }
3722
433
    nchan = getub(buf, 5);
3723
433
    if (nchan > MAXCHANNELS) {
3724
67
        GPSD_LOG(LOG_WARN, &session->context->errout,
3725
67
                 "UBX: NAV-SIG: nchan %u > MAXCHANNELS %d",
3726
67
                 nchan, MAXCHANNELS);
3727
67
        return 0;
3728
67
    }
3729
366
    if ((8 + 16 * nchan) > data_len) {
3730
232
        GPSD_LOG(LOG_WARN, &session->context->errout,
3731
232
                 "UBX: NAV-SIG: runt payload %zd channels %u\n",
3732
232
                 data_len, nchan);
3733
232
        return 0;
3734
232
    }
3735
3736
    // two "unused" bytes at buf[6:7]
3737
3738
    /* elevation and azimuth are in NAV-SAT, make a copy of any NAV-SAT
3739
     * data before initializiing it. */
3740
134
    memcpy(skyview_old, session->gpsdata.skyview, sizeof(skyview_old));
3741
3742
#ifdef __UNUSED__    // debug
3743
    GPSD_LOG(LOG_SHOUT, &session->context->errout,
3744
             "UBX: NAV-SIG: gpsd_zero_satellites()\n");
3745
#endif
3746
134
    gpsd_zero_satellites(&session->gpsdata);
3747
3748
2.62k
    for (i = 0; i < nchan; i++) {
3749
        // like NAV-SAT, but 16 bytes instead of 12, no elevation or azimuth
3750
2.49k
        char buf2[80];
3751
2.49k
        int sat_old;
3752
2.49k
        unsigned off = 8 + 16 * i;
3753
2.49k
        short nmea_PRN = 0;
3754
2.49k
        uint8_t gnssId = getub(buf, off + 0);
3755
2.49k
        uint8_t svId = getub(buf, off + 1);
3756
2.49k
        uint8_t sigId = getub(buf, off + 2);
3757
2.49k
        uint8_t freqid = getub(buf, off + 3);
3758
2.49k
        int16_t prRes = getles16(buf, off + 4);     // 0.1 m
3759
2.49k
        uint8_t cno = getub(buf, off + 6);          // dBHz
3760
2.49k
        uint8_t qualityInd = getub(buf, off + 7);   // quality indicator
3761
        // not exactly right?
3762
2.49k
        bool used = (4 <= qualityInd) ? true : false;
3763
2.49k
        uint8_t corrSource = getub(buf, off + 8);   // correlation source
3764
2.49k
        uint8_t ionoModel = getub(buf, off + 9);    // Ionospheric model used:
3765
2.49k
        unsigned sigFlags = getleu16(buf, off + 10);
3766
3767
        // last 4 bytes, reserved
3768
2.49k
        uint32_t reserved = getleu32(buf, 12);
3769
3770
2.49k
        nmea_PRN = ubx2_to_prn(gnssId, svId);
3771
2.49k
        if (0 >= nmea_PRN) {
3772
1.68k
            if (-1 == nmea_PRN) {
3773
                // ignore GLONASS 255
3774
90
                continue;
3775
90
            }
3776
            // bad PRN??
3777
1.59k
            GPSD_LOG(LOG_PROG, &session->context->errout,
3778
1.59k
                     "UBX: NAV-SIG(%d) Bad PRN: gnssid %u, svid %u PRN %d\n",
3779
1.59k
                     seen, gnssId, svId, nmea_PRN);
3780
1.59k
            continue;
3781
1.68k
        }
3782
3783
814
        session->gpsdata.skyview[seen].gnssid = gnssId;
3784
814
        session->gpsdata.skyview[seen].svid = svId;
3785
814
        session->gpsdata.skyview[seen].sigid = sigId;
3786
814
        session->gpsdata.skyview[seen].freqid = freqid;
3787
814
        session->gpsdata.skyview[seen].PRN = nmea_PRN;
3788
814
        session->gpsdata.skyview[seen].prRes = prRes / 10.0;
3789
814
        session->gpsdata.skyview[seen].qualityInd = qualityInd;
3790
3791
814
        session->gpsdata.skyview[seen].ss = (double)cno;
3792
814
        session->gpsdata.skyview[seen].used = used;
3793
        // sbas_in_use is not same as used
3794
814
        if (used) {
3795
491
            used_tot++;
3796
491
        }
3797
        // by some coincidence, our health flags matches u-blox's
3798
814
        session->gpsdata.skyview[seen].health = sigFlags & 3;
3799
        // try to keep elevation and azimuth from NAV-SAT
3800
814
        for (sat_old = 0; sat_old < MAXCHANNELS; sat_old++) {
3801
814
            if (0 >= skyview_old[sat_old].PRN) {
3802
                // end of list, not found
3803
814
                break;
3804
814
            }
3805
0
            if (nmea_PRN != skyview_old[sat_old].PRN) {
3806
                // not this one
3807
0
                continue;
3808
0
            }
3809
            // found it, grab the data
3810
0
            session->gpsdata.skyview[seen].azimuth =
3811
0
                skyview_old[sat_old].azimuth;
3812
0
            session->gpsdata.skyview[seen].elevation =
3813
0
                skyview_old[sat_old].elevation;
3814
0
            break;
3815
0
        }
3816
814
        GPSD_LOG(LOG_PROG, &session->context->errout,
3817
814
                 "UBX: NAV-SIG gnssid %u, svid %u sigid %u PRN %d freqid %u "
3818
814
                 "prRes %d cno %u qual %u corr %u, iono %u flags x%x res x%x "
3819
814
                 "az %.1f el %.1f\n",
3820
814
                 gnssId, svId, sigId, nmea_PRN, freqid, prRes, cno,
3821
814
                 qualityInd, corrSource, ionoModel, sigFlags, reserved,
3822
814
                 session->gpsdata.skyview[seen].azimuth,
3823
814
                 session->gpsdata.skyview[seen].elevation);
3824
814
        GPSD_LOG(LOG_IO, &session->context->errout,
3825
814
                 "UBX: NAV-SIG(%d): gnssId:%s flags:%s quality:%s "
3826
814
                 "courrSource:%s ionoModel:%s\n",
3827
814
                 seen, val2str(gnssId, vgnssId),
3828
814
                 flags2str(sigFlags, fsig_sigFlags, buf2, sizeof(buf2)),
3829
814
                 val2str(qualityInd, vquality),
3830
814
                 val2str(corrSource, vsig_corrsource),
3831
814
                 val2str(ionoModel, vsig_ionomodel));
3832
3833
814
        seen++;
3834
814
    }
3835
3836
134
    session->gpsdata.satellites_visible = seen;
3837
134
    session->gpsdata.satellites_used = used_tot;
3838
134
    GPSD_LOG(LOG_PROG, &session->context->errout,
3839
134
             "UBX: NAV-SIG: time %s visible=%d used=%d"
3840
134
             " mask={SATELLITE|USED}\n",
3841
134
             timespec_str(&session->gpsdata.skyview_time,
3842
134
                          ts_buf, sizeof(ts_buf)),
3843
134
             session->gpsdata.satellites_visible,
3844
134
             session->gpsdata.satellites_used);
3845
134
    return SATELLITE_SET | USED_IS;
3846
366
}
3847
3848
/**
3849
 * Navigation solution message: UBX-NAV-SOL
3850
 *
3851
 * Present in:
3852
 *    protVer 7 ( Antaris)
3853
 *    protVer up to 23,01
3854
 *
3855
 * Deprecated in:
3856
 *    protVer 13 (6-series)
3857
 *
3858
 * Not present in:
3859
 *    protVer 27 (9-series)
3860
 *    Use UBX-NAV-PVT instead
3861
 *
3862
 * UBX-NAV-SOL has ECEF and VECEF, so no need for UBX-NAV-POSECEF and
3863
 * UBX-NAV-VELECEF
3864
 */
3865
static gps_mask_t ubx_msg_nav_sol(struct gps_device_t *session,
3866
                                  unsigned char *buf, size_t data_len)
3867
509
{
3868
509
    char buf2[80];
3869
509
    unsigned flags, pdop;
3870
509
    unsigned gpsFix;
3871
509
    gps_mask_t mask = 0;
3872
509
    char ts_buf[TIMESPEC_LEN];
3873
3874
509
    if (52 > data_len) {
3875
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
3876
71
                 "UBX: NAV-SOL: runt payload len %zd", data_len);
3877
71
        return 0;
3878
71
    }
3879
3880
438
    session->driver.ubx.iTOW = getleu32(buf, 0);
3881
438
    gpsFix = getub(buf, 10);
3882
438
    flags = getub(buf, 11);
3883
438
    mask = 0;
3884
876
#define DATE_VALID      (UBX_SOL_VALID_WEEK | UBX_SOL_VALID_TIME)
3885
438
    if ((flags & DATE_VALID) == DATE_VALID) {
3886
335
        unsigned short week;
3887
335
        timespec_t ts_tow;
3888
3889
335
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
3890
335
        ts_tow.tv_nsec += (long)getles32(buf, 4);
3891
335
        TS_NORM(&ts_tow);
3892
335
        week = (unsigned short)getles16(buf, 8);
3893
335
        session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
3894
335
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
3895
335
    }
3896
438
#undef DATE_VALID
3897
3898
438
    session->newdata.ecef.x = getles32(buf, 12) / 100.0;
3899
438
    session->newdata.ecef.y = getles32(buf, 16) / 100.0;
3900
438
    session->newdata.ecef.z = getles32(buf, 20) / 100.0;
3901
438
    session->newdata.ecef.pAcc = getleu32(buf, 24) / 100.0;
3902
438
    session->newdata.ecef.vx = getles32(buf, 28) / 100.0;
3903
438
    session->newdata.ecef.vy = getles32(buf, 32) / 100.0;
3904
438
    session->newdata.ecef.vz = getles32(buf, 36) / 100.0;
3905
438
    session->newdata.ecef.vAcc = getleu32(buf, 40) / 100.0;
3906
438
    mask |= ECEF_SET | VECEF_SET;
3907
3908
438
    session->newdata.eps = (double)(getles32(buf, 40) / 100.0);
3909
438
    mask |= SPEEDERR_SET;
3910
3911
438
    pdop = getleu16(buf, 44);
3912
438
    if (9999 > pdop) {
3913
132
        session->gpsdata.dop.pdop = (double)(pdop / 100.0);
3914
132
        mask |= DOP_SET;
3915
132
    }
3916
438
    session->gpsdata.satellites_used = (int)getub(buf, 47);
3917
3918
438
    switch (gpsFix) {
3919
1
    case UBX_MODE_TMONLY:
3920
        // Surveyed-in, better not have moved
3921
1
        session->newdata.mode = MODE_3D;
3922
1
        session->newdata.status = STATUS_TIME;
3923
1
        break;
3924
74
    case UBX_MODE_3D:
3925
74
        session->newdata.mode = MODE_3D;
3926
74
        session->newdata.status = STATUS_GPS;
3927
74
        break;
3928
13
    case UBX_MODE_2D:
3929
13
        session->newdata.mode = MODE_2D;
3930
13
        session->newdata.status = STATUS_GPS;
3931
13
        break;
3932
15
    case UBX_MODE_DR:           // consider this too as 2D
3933
15
        session->newdata.mode = MODE_2D;
3934
15
        session->newdata.status = STATUS_DR;
3935
15
        break;
3936
1
    case UBX_MODE_GPSDR:        // DR-aided GPS is valid 3D
3937
1
        session->newdata.mode = MODE_3D;
3938
1
        session->newdata.status = STATUS_GNSSDR;
3939
1
        break;
3940
334
    default:
3941
334
        session->newdata.mode = MODE_NO_FIX;
3942
334
        session->newdata.status = STATUS_UNK;
3943
334
        break;
3944
438
    }
3945
3946
438
    if (0 != (flags & UBX_SOL_FLAG_DGPS))
3947
248
        session->newdata.status = STATUS_DGPS;
3948
3949
438
    mask |= MODE_SET | STATUS_SET;
3950
    // older u-blox, cycle ender may be iffy
3951
    // so err o nthe side of over-reporting TPV
3952
438
    mask |= REPORT_IS;
3953
3954
438
    GPSD_LOG(LOG_PROG, &session->context->errout,
3955
438
             "UBX: NAV-SOL: time=%s ecef x:%.2f y:%.2f z:%.2f track=%.2f "
3956
438
             "speed=%.2f mode=%d status=%d used=%d\n",
3957
438
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
3958
438
             session->newdata.ecef.x,
3959
438
             session->newdata.ecef.y,
3960
438
             session->newdata.ecef.z,
3961
438
             session->newdata.track,
3962
438
             session->newdata.speed,
3963
438
             session->newdata.mode,
3964
438
             session->newdata.status,
3965
438
             session->gpsdata.satellites_used);
3966
438
    GPSD_LOG(LOG_IO, &session->context->errout,
3967
438
             "UBX: NAV-SOL-PVT: gpsFix:%s flags:%s\n",
3968
438
             val2str(gpsFix, vpvt_fixType),
3969
438
             flags2str(flags, fhnr_pvt_flags, buf2, sizeof(buf2)));
3970
438
    return mask;
3971
438
}
3972
3973
3974
/**
3975
 * Receiver navigation status
3976
 * UBX-NAV-STATUS Class 1, ID 3
3977
 *
3978
 * Present in Antaris to 9-series
3979
 */
3980
static gps_mask_t
3981
ubx_msg_nav_status(struct gps_device_t *session, unsigned char *buf,
3982
                   size_t data_len)
3983
841
{
3984
841
    uint8_t gpsFix;
3985
841
    uint8_t flags;
3986
841
    uint8_t fixStat;
3987
841
    uint8_t flags2;
3988
841
    uint32_t ttff;
3989
841
    uint32_t msss;
3990
841
    int *status = &session->newdata.status;
3991
841
    int *mode = &session->newdata.mode;
3992
841
    gps_mask_t mask = 0;
3993
3994
841
    if (16 > data_len) {
3995
70
        GPSD_LOG(LOG_WARN, &session->context->errout,
3996
70
                 "UBX: NAV-STATUS: runt payload len %zd", data_len);
3997
70
        return 0;
3998
70
    }
3999
4000
771
    session->driver.ubx.iTOW = getleu32(buf, 0);
4001
771
    gpsFix = getub(buf, 4);
4002
771
    flags = getub(buf, 5);
4003
771
    fixStat = getub(buf, 6);
4004
771
    flags2 = getub(buf, 7);
4005
771
    ttff = getleu32(buf, 8);
4006
771
    msss = getleu32(buf, 12);
4007
4008
    // FIXME: how does this compare with other places ubx sets mode/status?
4009
771
    if (0 == (1 & flags)) {
4010
        // gpsFix not OK
4011
205
        *mode = MODE_NO_FIX;
4012
205
        *status = STATUS_UNK;
4013
566
    } else {
4014
566
        switch (gpsFix) {
4015
1
        case UBX_MODE_TMONLY:
4016
            // 5 - Surveyed-in, so a precise 3D.
4017
1
            *mode = MODE_3D;
4018
1
            *status = STATUS_TIME;
4019
1
            break;
4020
4021
135
        case UBX_MODE_3D:
4022
            // 3
4023
135
            *mode = MODE_3D;
4024
135
            *status = STATUS_GPS;
4025
135
            break;
4026
4027
1
        case UBX_MODE_GPSDR:
4028
            // 4
4029
1
            *mode = MODE_3D;
4030
1
            *status = STATUS_GNSSDR;
4031
1
            break;
4032
4033
218
        case UBX_MODE_2D:
4034
            // 2
4035
218
            *mode = MODE_2D;
4036
218
            if (2 == (2 & fixStat)) {
4037
140
                *status = STATUS_DGPS;
4038
140
            } else {
4039
78
                *status = STATUS_GPS;
4040
78
            }
4041
218
            break;
4042
4043
71
        case UBX_MODE_DR:           // consider this too as 2D
4044
            // 1
4045
71
            *mode = MODE_2D;
4046
71
            *status = STATUS_DR;
4047
71
            break;
4048
4049
98
        case UBX_MODE_NOFIX:
4050
            // 0
4051
98
            FALLTHROUGH
4052
140
        default:
4053
            // > 5, huh??
4054
140
            *mode = MODE_NO_FIX;
4055
140
            *status = STATUS_UNK;
4056
140
            break;
4057
566
        }
4058
566
        if (2 == (2 & fixStat)) {
4059
215
            if (0x40 == (0x40 & flags2)) {
4060
71
                *status = STATUS_RTK_FLT;
4061
144
            } else if (0x80 == (0x80 & flags2)) {
4062
67
                *status = STATUS_RTK_FIX;
4063
67
            }
4064
            // else ??
4065
351
        } else if (1 == (1 & fixStat)) {
4066
99
            *status = STATUS_DGPS;
4067
99
        }
4068
566
    }
4069
771
    mask |= STATUS_SET | MODE_SET;
4070
4071
771
    GPSD_LOG(LOG_PROG, &session->context->errout,
4072
771
         "UBX: NAV-STATUS: iTOW %lld gpsFix %u flags %02x fixStat %02x "
4073
771
         "flags2=%02x ttff=%llu msss=%llu mode=%u status=%u\n",
4074
771
         (long long)session->driver.ubx.iTOW,
4075
771
         gpsFix,
4076
771
         flags,
4077
771
         fixStat,
4078
771
         flags2,
4079
771
         (long long unsigned)ttff,
4080
771
         (long long unsigned)msss,
4081
771
         session->newdata.mode,
4082
771
         session->newdata.status);
4083
771
    return mask;
4084
771
}
4085
4086
/**
4087
 * Survey-in data - UBX-NAV-SVIN
4088
 * Time Sync products only
4089
 */
4090
static gps_mask_t ubx_msg_nav_svin(struct gps_device_t *session,
4091
                                   unsigned char *buf, size_t data_len UNUSED)
4092
3
{
4093
3
    gps_mask_t mask = ONLINE_SET;
4094
3
    unsigned version = getub(buf, 0);
4095
    // 3 reserved bytes
4096
3
    unsigned long iTOW = getleu32(buf, 4);
4097
3
    unsigned long dur = getleu32(buf, 0);
4098
3
    long long meanX = getles32(buf, 12);             // cm
4099
3
    long long meanY = getles32(buf, 16);             // cm
4100
3
    long long meanZ = getles32(buf, 20);             // cm
4101
3
    int meanXHP = getsb(buf, 24);                    // 0.1 mm
4102
3
    int meanYHP = getsb(buf, 25);                    // 0.1 mm
4103
3
    int meanZHP = getsb(buf, 26);                    // 0.1 mm
4104
    // 1 reserved byte
4105
3
    unsigned long meanAcc = getleu32(buf, 28);  // 0.1 mm
4106
3
    unsigned long obs = getleu32(buf, 32);
4107
3
    unsigned valid = getub(buf, 36);
4108
3
    unsigned active = getub(buf, 37);
4109
    // 2 reserved
4110
4111
    // Only version 0 is defined up to ub-blox 9
4112
3
    if (0 != version) {
4113
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
4114
2
                 "UBX: NAV-SVIN: unknown version $u %u", version);
4115
2
        return 0;
4116
2
    }
4117
4118
1
    session->driver.ubx.iTOW = iTOW;
4119
1
    meanX = (meanX * 10) + meanXHP;
4120
1
    meanY = (meanY * 10) + meanYHP;
4121
1
    meanZ = (meanZ * 10) + meanZHP;
4122
4123
    // casts for 32 bit compatibility
4124
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
4125
1
             "UBX: NAV-SVIN: iTOW %llu dur=%lu meanX=%lld meanY=%lld "
4126
1
             "meanZ=%lld meanAcc=%lu "
4127
1
             "obs=%lu valid=%u(%s) active=%u(%s)\n",
4128
1
             (long long)iTOW, dur, meanX, meanY, meanZ,
4129
1
             meanAcc, obs, valid, val2str(valid, vsvin_valid),
4130
1
             active, val2str(active, vsvin_active));
4131
1
    return mask;
4132
3
}
4133
4134
/**
4135
 * GPS Satellite Info -- deprecated - UBX-NAV-SVINFO
4136
 * Present in:
4137
 *    protver < 27
4138
 * Not present in:
4139
      protver >= 27 (9-series), use UBX-NAV-SAT instead
4140
 */
4141
static gps_mask_t ubx_msg_nav_svinfo(struct gps_device_t *session,
4142
                                     unsigned char *buf, size_t data_len)
4143
650
{
4144
650
    char buf2[80];
4145
650
    unsigned i, nchan;
4146
650
    int seen = 0, used_tot = 0;
4147
650
    unsigned chipGen;
4148
650
    unsigned globalFlags;
4149
650
    timespec_t ts_tow;
4150
    // chipGen to protVer, Antaris 4, u-blox 4, 5, 6, 7 and 8
4151
650
    static unsigned gen2ver[] = {8, 10, 12, 13, 15};
4152
650
    char ts_buf[TIMESPEC_LEN];
4153
4154
650
    if (8 > data_len) {
4155
71
        GPSD_LOG(LOG_PROG, &session->context->errout,
4156
71
                 "UBX: NAV-SVINFO runt datalen %zd\n", data_len);
4157
71
        return 0;
4158
71
    }
4159
4160
579
    session->driver.ubx.iTOW = getleu32(buf, 0);
4161
579
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
4162
579
    session->gpsdata.skyview_time =
4163
579
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
4164
4165
579
    nchan = getub(buf, 4);
4166
579
    if (nchan > MAXCHANNELS) {
4167
39
        GPSD_LOG(LOG_WARN, &session->context->errout,
4168
39
                 "UBX: NAV SVINFO: too many channels %u\n",
4169
39
                 nchan);
4170
39
        return 0;
4171
39
    }
4172
540
    if ((8 + 12 * nchan) > data_len) {
4173
76
        GPSD_LOG(LOG_WARN, &session->context->errout,
4174
76
                 "UBX: NAV-SVINFO: runt payload %zd channels %u\n",
4175
76
                 data_len, nchan);
4176
76
        return 0;
4177
76
    }
4178
464
    globalFlags = getub(buf, 5);
4179
464
    chipGen = globalFlags & 0x07;
4180
464
    if (ROWS(gen2ver) > chipGen &&
4181
        // put a floor under protVer
4182
386
        gen2ver[chipGen] > session->driver.ubx.protver) {
4183
189
        session->driver.ubx.protver = gen2ver[chipGen];
4184
189
    }
4185
4186
#ifdef __UNUSED__    // debug
4187
    GPSD_LOG(LOG_SHOUT, &session->context->errout,
4188
             "UBX: NAV-SVINFO: gpsd_zero_satellites()\n");
4189
#endif
4190
464
    gpsd_zero_satellites(&session->gpsdata);
4191
4192
35.5k
    for (i = 0; i < nchan; i++) {
4193
35.1k
        unsigned off = 8 + 12 * i;
4194
35.1k
        short nmea_PRN;
4195
35.1k
        unsigned chan = getub(buf, off);
4196
35.1k
        unsigned ubx_PRN = getub(buf, off + 1);
4197
35.1k
        unsigned flags = getub(buf, off + 2);
4198
35.1k
        unsigned quality = getub(buf, off + 3);
4199
35.1k
        unsigned cno = getub(buf, off + 4);
4200
35.1k
        bool used = (bool)(flags & 0x01);
4201
35.1k
        int el = getsb(buf, off + 5);
4202
35.1k
        int az = getles16(buf, off + 6);
4203
35.1k
        int prRes = getles16(buf, off + 7);
4204
4205
35.1k
        nmea_PRN = ubx_to_prn(ubx_PRN,
4206
35.1k
                              &session->gpsdata.skyview[seen].gnssid,
4207
35.1k
                              &session->gpsdata.skyview[seen].svid);
4208
4209
35.1k
        if (1 > nmea_PRN) {
4210
            // skip bad PRN
4211
26.7k
            GPSD_LOG(LOG_PROG, &session->context->errout,
4212
26.7k
                     "UBX: NAV-SVINFO bad NMEA PRN %d\n", nmea_PRN);
4213
26.7k
            continue;
4214
26.7k
        }
4215
8.34k
        session->gpsdata.skyview[seen].PRN = nmea_PRN;
4216
4217
8.34k
        session->gpsdata.skyview[seen].ss = (double)cno;
4218
8.34k
        if (90 >= abs(el)) {
4219
6.14k
            session->gpsdata.skyview[seen].elevation = (double)el;
4220
6.14k
        }
4221
8.34k
        if (360 > az &&
4222
5.17k
            0 <= az) {
4223
1.16k
            session->gpsdata.skyview[seen].azimuth = (double)az;
4224
1.16k
        }
4225
8.34k
        session->gpsdata.skyview[seen].prRes = prRes / 100.0;
4226
8.34k
        session->gpsdata.skyview[seen].qualityInd = quality;
4227
8.34k
        session->gpsdata.skyview[seen].used = used;
4228
        // sbas_in_use is not same as used
4229
8.34k
        if (used) {
4230
            // not really 'used', just integrity data from there
4231
5.18k
            used_tot++;
4232
5.18k
        }
4233
8.34k
        if (0x10 == (0x10 & flags)) {
4234
3.18k
           session->gpsdata.skyview[seen].health = SAT_HEALTH_BAD;
4235
5.16k
        } else {
4236
5.16k
           session->gpsdata.skyview[seen].health = SAT_HEALTH_OK;
4237
5.16k
        }
4238
4239
8.34k
        GPSD_LOG(LOG_PROG, &session->context->errout,
4240
8.34k
                 "UBX: NAV-SVINFO chan %u ubx_prn %d gnssid %d svid %d "
4241
8.34k
                 "nmea_PRN %d flags x%x az %.0f el %.0f cno %.0f prRes %.2f "
4242
8.34k
                 "quality %u\n",
4243
8.34k
                 chan, ubx_PRN,
4244
8.34k
                 session->gpsdata.skyview[seen].gnssid,
4245
8.34k
                 session->gpsdata.skyview[seen].svid, nmea_PRN, flags,
4246
8.34k
                 session->gpsdata.skyview[seen].azimuth,
4247
8.34k
                 session->gpsdata.skyview[seen].elevation,
4248
8.34k
                 session->gpsdata.skyview[seen].ss,
4249
8.34k
                 session->gpsdata.skyview[seen].prRes,
4250
8.34k
                 session->gpsdata.skyview[seen].qualityInd);
4251
8.34k
        GPSD_LOG(LOG_IO, &session->context->errout,
4252
8.34k
                 "UBX: NAV-SVINFO: flags (%s) quality %s\n",
4253
8.34k
                 flags2str(flags, fsvinfo_flags, buf2, sizeof(buf2)),
4254
8.34k
                 val2str(quality, vquality));
4255
4256
8.34k
        seen++;
4257
8.34k
    }
4258
4259
464
    session->gpsdata.satellites_visible = seen;
4260
464
    session->gpsdata.satellites_used = used_tot;
4261
464
    GPSD_LOG(LOG_PROG, &session->context->errout,
4262
464
             "UBX: NAV-SVINFO: time %s visible %d used %d "
4263
464
             " mask {SATELLITE|USED} gFlags x%x\n",
4264
464
             timespec_str(&session->gpsdata.skyview_time,
4265
464
                          ts_buf, sizeof(ts_buf)),
4266
464
             session->gpsdata.satellites_visible,
4267
464
             session->gpsdata.satellites_used,
4268
464
             globalFlags);
4269
464
    GPSD_LOG(LOG_IO, &session->context->errout,
4270
464
             "UBX: NAV-SVINFO: chipGen %s\n",
4271
464
             val2str(chipGen, vglobalFlags));
4272
464
    return SATELLITE_SET | USED_IS;
4273
540
}
4274
4275
/**
4276
 * GPS Leap Seconds - UBX-NAV-TIMEGPS
4277
 * Present in:
4278
 *     protVer 8 (Antaris 4)
4279
 *     protVer 27 (F9P)
4280
 *     protVer 34 (M10)
4281
 *
4282
 * Not in:
4283
 *     protVer 24 (NEO-D9S)
4284
 */
4285
static gps_mask_t ubx_msg_nav_timegps(struct gps_device_t *session,
4286
                                      unsigned char *buf, size_t data_len)
4287
635
{
4288
635
    char buf2[80];
4289
635
    uint8_t valid;         // Validity Flags
4290
635
    gps_mask_t mask = 0;
4291
635
    char ts_buf[TIMESPEC_LEN];
4292
4293
635
    if (16 > data_len) {
4294
104
        GPSD_LOG(LOG_WARN, &session->context->errout,
4295
104
                 "UBX: NAV-TIMEGPS: runt payload len %zd", data_len);
4296
104
        return 0;
4297
104
    }
4298
4299
531
    session->driver.ubx.iTOW = getleu32(buf, 0);
4300
531
    valid = getub(buf, 11);
4301
    // Valid leap seconds ?
4302
531
    if ((valid & UBX_TIMEGPS_VALID_LEAP_SECOND) ==
4303
531
        UBX_TIMEGPS_VALID_LEAP_SECOND) {
4304
172
        session->context->leap_seconds = (int)getub(buf, 10);
4305
172
        session->context->valid |= LEAP_SECOND_VALID;
4306
172
    }
4307
    // Valid GPS time of week and week number
4308
1.06k
#define VALID_TIME (UBX_TIMEGPS_VALID_TIME | UBX_TIMEGPS_VALID_WEEK)
4309
531
    if ((valid & VALID_TIME) == VALID_TIME) {
4310
461
#undef VALID_TIME
4311
461
        uint16_t week;
4312
461
        double tAcc;      // Time Accuracy Estimate in ns
4313
461
        timespec_t ts_tow;
4314
4315
461
        week = getles16(buf, 8);
4316
461
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
4317
461
        ts_tow.tv_nsec += (long)getles32(buf, 4);
4318
461
        TS_NORM(&ts_tow);
4319
461
        session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
4320
4321
461
        tAcc = (double)getleu32(buf, 12);     // tAcc in ns
4322
461
        session->newdata.ept = tAcc / 1e9;
4323
461
        mask |= (TIME_SET | NTPTIME_IS);
4324
461
    }
4325
4326
531
    GPSD_LOG(LOG_PROG, &session->context->errout,
4327
531
             "UBX: NAV-TIMEGPS: time=%s mask={TIME} valid x%x\n",
4328
531
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
4329
531
             valid);
4330
531
    GPSD_LOG(LOG_IO, &session->context->errout,
4331
531
             "UBX: NAV-TIMEGPS: valid %s\n",
4332
531
             flags2str(valid, vtimegps_valid, buf2, sizeof(buf2)));
4333
531
    return mask;
4334
635
}
4335
4336
/**
4337
 * Navigation time to leap second: UBX-NAV-TIMELS
4338
 *
4339
 * Sets leap_notify if leap second is < 23 hours away.
4340
 * Present in:
4341
 *     protVer 15 (8-series)
4342
 * Not in:
4343
 *     protVer 12 (5-series)
4344
 *     protVer 13 (6-series)
4345
 *     protVer 14 (6-series / GLONASS, 6-series)
4346
 */
4347
static gps_mask_t ubx_msg_nav_timels(struct gps_device_t *session,
4348
                                     unsigned char *buf, size_t data_len)
4349
294
{
4350
294
    char buf2[80];
4351
294
    unsigned version;
4352
294
    unsigned valid;
4353
294
    int valid_curr_ls;
4354
294
    int valid_time_to_ls_event;
4355
4356
294
#define UBX_TIMELS_VALID_CURR_LS 0x01
4357
294
#define UBX_TIMELS_VALID_TIME_LS_EVT 0x01
4358
4359
294
    if (24 > data_len) {
4360
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
4361
1
                 "UBX: NAV-TIMELS: runt %zd, expecting 24\n",
4362
1
                 data_len);
4363
1
        return 0;
4364
1
    }
4365
4366
293
    session->driver.ubx.iTOW = getleu32(buf, 0);
4367
293
    version = getub(buf, 4);
4368
    // Only version 0 is defined up to ub-blox 9
4369
293
    if (0 != version) {
4370
89
        GPSD_LOG(LOG_WARN, &session->context->errout,
4371
89
                 "UBX: NAV-TIMELS: unknown version $u %u", version);
4372
89
        return 0;
4373
89
    }
4374
204
    valid = getub(buf, 23);
4375
204
    GPSD_LOG(LOG_PROG, &session->context->errout,
4376
204
             "UBX: NAV-TIMELS: valid x%x version %d\n", valid, version);
4377
204
    GPSD_LOG(LOG_INF, &session->context->errout,
4378
204
             "UBX: NAV-TIMELS: valid %s\n",
4379
204
             flags2str(valid, vtimels_valid, buf2, sizeof(buf2)));
4380
4381
204
    valid_curr_ls = valid & UBX_TIMELS_VALID_CURR_LS;
4382
204
    valid_time_to_ls_event = valid & UBX_TIMELS_VALID_TIME_LS_EVT;
4383
204
    if (valid_curr_ls) {
4384
137
        unsigned int src_of_curr_ls = getub(buf,8);
4385
137
        int curr_ls = getsb(buf,9);
4386
4387
137
        GPSD_LOG(LOG_PROG, &session->context->errout,
4388
137
                 "UBX: NAV-TIMELS: srcOfCurrLs %u(%s) curr_ls %d\n",
4389
137
                 src_of_curr_ls,
4390
137
                 val2str(src_of_curr_ls, vsrcOfCurrLs),
4391
137
                 curr_ls);
4392
137
        session->context->leap_seconds = curr_ls;
4393
137
        session->context->valid |= LEAP_SECOND_VALID;
4394
137
    }  // Valid current leap second
4395
4396
204
    if (valid_time_to_ls_event) {
4397
137
        unsigned int src_of_ls_change;
4398
137
        unsigned short dateOfLSGpsWn, dateOfLSGpsDn;
4399
137
        int lsChange = getsb(buf, 11);
4400
137
        int timeToLsEvent = getles32(buf, 12);
4401
4402
137
        src_of_ls_change = getub(buf,10);
4403
4404
137
        dateOfLSGpsWn = getles16(buf,16);
4405
137
        dateOfLSGpsDn = getles16(buf,18);
4406
137
        GPSD_LOG(LOG_PROG, &session->context->errout,
4407
137
                 "UBX: NAV-TIMELS: srcOfCurLsChange %u(%s) lsChange %d "
4408
137
                 "timeToLsEvent %d dateOfLSGpsWn %d dateOfLSGpsDn %d\n",
4409
137
                 src_of_ls_change,
4410
137
                 val2str(src_of_ls_change, vsrcOfLsChange),
4411
137
                 lsChange, timeToLsEvent,
4412
137
                 dateOfLSGpsWn,dateOfLSGpsDn);
4413
4414
137
        if ((0 != lsChange) && (0 < timeToLsEvent) &&
4415
100
            ((60 * 60 * 23) > timeToLsEvent)) {
4416
52
            if (1 == lsChange) {
4417
2
                session->context->leap_notify = LEAP_ADDSECOND;
4418
2
                GPSD_LOG(LOG_WARN, &session->context->errout,
4419
2
                         "UBX: NAV-TIMELS: leap_notify %d "
4420
2
                         "Positive leap second today\n",
4421
2
                         session->context->leap_notify);
4422
50
            } else if (-1 == lsChange) {
4423
1
                session->context->leap_notify = LEAP_DELSECOND;
4424
1
                GPSD_LOG(LOG_WARN, &session->context->errout,
4425
1
                         "UBX: NAV-TIMELS:leap_notify %d "
4426
1
                         " Negative leap second today\n",
4427
1
                         session->context->leap_notify);
4428
1
            }
4429
85
        } else {
4430
85
            session->context->leap_notify = LEAP_NOWARNING;
4431
85
            GPSD_LOG(LOG_PROG, &session->context->errout,
4432
85
                     "UBX: NAV-TIMELS: leap_notify %d, none today\n",
4433
85
                     session->context->leap_notify);
4434
85
        }
4435
137
    }
4436
204
    return 0;
4437
293
}
4438
4439
/**
4440
 * UBX-NAV-TIMEUTC
4441
 */
4442
static gps_mask_t ubx_msg_nav_timeutc(struct gps_device_t *session,
4443
                                      unsigned char *buf, size_t data_len)
4444
525
{
4445
525
    uint8_t valid;         // Validity Flags
4446
525
    gps_mask_t mask = 0;
4447
4448
525
    if (20 > data_len) {
4449
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
4450
71
                 "UBX: NAV-TIMEUTC: runt payload len %zd", data_len);
4451
71
        return 0;
4452
71
    }
4453
4454
454
    session->driver.ubx.iTOW = getleu32(buf, 0);
4455
454
    valid = getub(buf, 19);
4456
454
    if (4 == (4 & valid)) {
4457
        // UTC is valid
4458
412
        struct tm date = {0};
4459
        // mask |= (TIME_SET | NTPTIME_IS);
4460
412
        uint32_t tAcc = getleu32(buf, 4);          // tAcc in ns
4461
        // nano can be negative, so this is not normalized UTC.
4462
412
        int32_t nano = getles32(buf, 8);           // fract sec in ns
4463
412
        date.tm_year = getleu16(buf, 12) - 1900;   // year, 1999..2099
4464
412
        date.tm_mon = getub(buf, 14) - 1;          // month 1..12
4465
412
        date.tm_mday = getub(buf, 15);             // day 1..31
4466
412
        date.tm_hour = getub(buf, 16);             // hour 0..23
4467
412
        date.tm_min = getub(buf, 17);              // min 0..59
4468
412
        date.tm_sec = getub(buf, 18);              // sec 0..60
4469
412
        session->newdata.time.tv_sec = mkgmtime(&date);
4470
412
        session->newdata.time.tv_nsec = nano;
4471
        // nano, can be negative! So normalize
4472
412
        TS_NORM(&session->newdata.time);
4473
        // other timestamped messages lack nano, so time will jump around...
4474
412
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
4475
4476
412
        GPSD_LOG(LOG_PROG, &session->context->errout,
4477
412
                 "UBX: NAV-TIMEUTC: iTOW=%lld valid=%02x %04d-%02d-%02d "
4478
412
                 "%02d:%02d:%02d.%09d tAcc=%llu time %lld.%09lld\n",
4479
412
                 (long long)session->driver.ubx.iTOW,
4480
412
                 valid, date.tm_year + 1900, date.tm_mon + 1, date.tm_mday,
4481
412
                 date.tm_hour, date.tm_min, date.tm_sec, nano,
4482
412
                 (long long unsigned)tAcc,
4483
412
                 (long long)session->newdata.time.tv_sec,
4484
412
                 (long long)session->newdata.time.tv_nsec);
4485
412
    } else {
4486
42
        GPSD_LOG(LOG_PROG, &session->context->errout,
4487
42
                 "UBX: NAV-TIMEUTC: iTOW=%lld valid=%02x\n",
4488
42
                 (long long)session->driver.ubx.iTOW,
4489
42
                 valid);
4490
42
    }
4491
454
    return mask;
4492
525
}
4493
4494
/*
4495
 * Velocity Position ECEF message, UBX-NAV-VELECEF
4496
 */
4497
static gps_mask_t ubx_msg_nav_velecef(struct gps_device_t *session,
4498
                                      unsigned char *buf, size_t data_len)
4499
113
{
4500
113
    gps_mask_t mask = VECEF_SET;
4501
4502
113
    if (20 > data_len) {
4503
78
        GPSD_LOG(LOG_WARN, &session->context->errout,
4504
78
                 "UBX: NAV-VELECEF: runt payload len %zd", data_len);
4505
78
        return 0;
4506
78
    }
4507
4508
35
    session->driver.ubx.iTOW = getleu32(buf, 0);
4509
35
    session->newdata.ecef.vx = getles32(buf, 4) / 100.0;
4510
35
    session->newdata.ecef.vy = getles32(buf, 8) / 100.0;
4511
35
    session->newdata.ecef.vz = getles32(buf, 12) / 100.0;
4512
35
    session->newdata.ecef.vAcc = getleu32(buf, 16) / 100.0;
4513
35
    GPSD_LOG(LOG_PROG, &session->context->errout,
4514
35
        "UBX: NAV-VELECEF: iTOW=%lld ECEF vx=%.2f vy=%.2f vz=%.2f vAcc=%.2f\n",
4515
35
        (long long)session->driver.ubx.iTOW,
4516
35
        session->newdata.ecef.vx,
4517
35
        session->newdata.ecef.vy,
4518
35
        session->newdata.ecef.vz,
4519
35
        session->newdata.ecef.vAcc);
4520
35
    return mask;
4521
113
}
4522
4523
/*
4524
 * Velocity NED message, UBX-NAV-VELNED
4525
 * protocol versions 15+
4526
 */
4527
static gps_mask_t ubx_msg_nav_velned(struct gps_device_t *session,
4528
                                     unsigned char *buf, size_t data_len)
4529
129
{
4530
129
    gps_mask_t mask = VNED_SET;
4531
4532
129
    if (36 > data_len) {
4533
89
        GPSD_LOG(LOG_WARN, &session->context->errout,
4534
89
                 "UBX: NAV-VELNED: runt payload len %zd", data_len);
4535
89
        return 0;
4536
89
    }
4537
4538
40
    session->driver.ubx.iTOW = getleu32(buf, 0);
4539
40
    session->newdata.NED.velN = getles32(buf, 4) / 100.0;
4540
40
    session->newdata.NED.velE = getles32(buf, 8) / 100.0;
4541
40
    session->newdata.NED.velD = getles32(buf, 12) / 100.0;
4542
    // ignore speed for now
4543
40
    GPSD_LOG(LOG_PROG, &session->context->errout,
4544
40
        "UBX: NAV-VELNED: iTOW=%lld NED velN=%.2f velE=%.2f velD=%.2f\n",
4545
40
         (long long)session->driver.ubx.iTOW,
4546
40
        session->newdata.NED.velN,
4547
40
        session->newdata.NED.velE,
4548
40
        session->newdata.NED.velD);
4549
40
    return mask;
4550
129
}
4551
4552
// UBX-MON-COMMS txErrors
4553
static const struct flist_t rxm_cor_statusInfo[] = {
4554
    {0, 0x1f, "UNk proto,"},
4555
    {1, 0x1f, "RTCM3,"},
4556
    {2, 0x1f, "SPARTN,"},
4557
    {29, 0x1f, "RXM-PMP,"},
4558
    {30, 0x1f, "RXM-QZSSL6,"},
4559
    {0, 0x60, "Err Unk,"},
4560
    {0x20, 0x60, "No Err,"},
4561
    {0x40, 0x60, "Error,"},
4562
    {0x80, 0x180, "Unused,"},
4563
    {0x100, 0x180, "Used,"},
4564
    // bits 9 to 24, correction Id
4565
    {0x200000, 0x200000, "msgTypeValid"},
4566
};
4567
4568
static const struct vlist_t rxm_spart_flags[] = {
4569
    {0, "Unknown"},
4570
    {1, "Not Used"},
4571
    {2, "Used"},
4572
    {3, "Reserved"},
4573
};
4574
4575
static const struct vlist_t spartn_mtypes[] = {
4576
    {0, "Orbit"},
4577
    {1, "HPAC"},
4578
    {2, "GAD"},
4579
    {3, "BDS"},
4580
};
4581
4582
static const struct vlist_t spartn_mstypes[] = {
4583
    {0, "GPS"},
4584
    {1, "GLO"},
4585
    {2, "GAL"},
4586
    {3, "BDS"},
4587
};
4588
4589
/**
4590
 * UBX-RXM-COR -- Differential Correction Input Messages
4591
 *
4592
 * Present in ZED-F9P, HPG 1.50, protVer 27.50
4593
 */
4594
static gps_mask_t ubx_msg_rxm_cor(struct gps_device_t *session,
4595
                                  unsigned char *buf, size_t data_len UNUSED)
4596
2
{
4597
2
    unsigned version = getub(buf, 0);
4598
2
    unsigned ebno = getub(buf, 1);
4599
2
    unsigned long statusInfo = getleu32(buf, 4);
4600
2
    unsigned msgType = getleu16(buf, 8);
4601
2
    unsigned msgsubType = getleu16(buf, 10);
4602
2
    char buf2[80];
4603
4604
2
    if (1 != version) {
4605
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
4606
1
                 "UBX: RXM-COR, unknown version %u\n", version);
4607
1
        return 0;
4608
1
    }
4609
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
4610
1
             "UBX: RXM-COR, version %u ebno %u statusInfo x%lx "
4611
1
             "msgtype %u msgsubType %u\n", version, ebno, statusInfo,
4612
1
             msgType, msgsubType);
4613
1
    GPSD_LOG(LOG_IO, &session->context->errout,
4614
1
            "UBX: RXM-COR: statusInfo (%s)  msgType (%s) msgsubType (%s)\n",
4615
1
            flags2str(statusInfo, rxm_cor_statusInfo, buf2, sizeof(buf2)),
4616
1
            val2str(msgType, spartn_mtypes),
4617
1
            val2str(msgsubType, spartn_mstypes));
4618
1
    return 0;
4619
2
}
4620
4621
/*
4622
 * Multi-GNSS Raw measurement Data -- UBX-RXM-RAWX
4623
 * Not in u-blox 5, 6 or 7
4624
 * u-blox 9, message version 0 (but no version byte!)
4625
 * u-blox 9, message version 1
4626
 */
4627
static gps_mask_t ubx_msg_rxm_rawx(struct gps_device_t *session,
4628
                                   const unsigned char *buf,
4629
                                   size_t data_len)
4630
537
{
4631
537
    double rcvTow;
4632
537
    uint16_t week;
4633
537
    int8_t leapS;
4634
537
    unsigned numMeas;
4635
537
    uint8_t recStat;
4636
537
    uint8_t version;
4637
537
    unsigned i;
4638
537
    const char * obs_code;
4639
537
    timespec_t ts_tow;
4640
4641
537
    if (16 > data_len) {
4642
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
4643
71
                 "UBX: RXM-RAWX: runt payload len %zd", data_len);
4644
71
        return 0;
4645
71
    }
4646
4647
    // zero the raw data area.
4648
466
    memset((void *)&session->gpsdata.raw, 0, sizeof(session->gpsdata.raw));
4649
4650
    // Note: this is "approximately" GPS TOW, this is not iTOW
4651
466
    rcvTow = getled64((const char *)buf, 0);   // time of week in seconds
4652
466
    week = getleu16(buf, 8);
4653
466
    leapS = getsb(buf, 10);
4654
466
    numMeas = getub(buf, 11);
4655
466
    recStat = getub(buf, 12);
4656
    /* byte 13 is version on u-blox 9, reserved on u-blox 8
4657
     * how is that supposed to work?? */
4658
466
    version = getub(buf, 13);
4659
4660
466
    GPSD_LOG(LOG_PROG, &session->context->errout,
4661
466
             "UBX: RXM-RAWX: rcvTow %f week %u leapS %d numMeas %u recStat %d"
4662
466
             " version %u\n",
4663
466
             rcvTow, week, leapS, numMeas, recStat, version);
4664
4665
466
    if (recStat & 1) {
4666
        // Valid leap seconds
4667
126
        session->context->leap_seconds = leapS;
4668
126
        session->context->valid |= LEAP_SECOND_VALID;
4669
126
    }
4670
    // RINEX 3 wants GPS time, not UTC time, do not add leap seconds.
4671
466
    DTOTS(&ts_tow, rcvTow);
4672
    // Do not set newdata.time.  set gpsdata.raw.mtime
4673
    // RINEX 3 "GPS time", not UTC, no leap seconds
4674
466
    session->gpsdata.raw.mtime = gpsd_gpstime(session, week, ts_tow);
4675
4676
466
    if (numMeas > MAXCHANNELS) {
4677
55
        GPSD_LOG(LOG_WARN, &session->context->errout,
4678
55
                 "UBX: RXM-RAWX: too many measurements (%u)",
4679
55
                 numMeas);
4680
55
        return 0;
4681
55
    }
4682
411
    if ((16 + 32 * numMeas) > data_len) {
4683
159
        GPSD_LOG(LOG_WARN, &session->context->errout,
4684
159
                 "UBX: RXM-RAWX: runt payload %zu numMeas %u\n",
4685
159
                 data_len, numMeas);
4686
159
        return 0;
4687
159
    }
4688
4689
2.84k
    for (i = 0; i < numMeas; i++) {
4690
2.59k
        int off = 32 * i;
4691
        // pseudorange in meters
4692
2.59k
        double prMes = getled64((const char *)buf, off + 16);
4693
        // carrier phase in cycles
4694
2.59k
        double cpMes = getled64((const char *)buf, off + 24);
4695
        // doppler in Hz, positive towards sat
4696
2.59k
        double doMes = getlef32((const char *)buf, off + 32);
4697
2.59k
        uint8_t gnssId = getub(buf, off + 36);
4698
2.59k
        uint8_t svId = getub(buf, off + 37);
4699
        // reserved in u-blox 8, sigId in u-blox 9 (version 1)
4700
2.59k
        uint8_t sigId = getub(buf, off + 38);
4701
        // GLONASS frequency slot
4702
2.59k
        uint8_t freqId = getub(buf, off + 39);
4703
        // carrier phase locktime in ms, max 64500ms
4704
2.59k
        uint16_t locktime = getleu16(buf, off + 40);
4705
        // carrier-to-noise density ratio dB-Hz
4706
2.59k
        uint8_t cno = getub(buf, off + 42);
4707
2.59k
        uint8_t prStdev = getub(buf, off + 43) & 0x0f;
4708
2.59k
        uint8_t cpStdev = getub(buf, off + 44) & 0x0f;
4709
2.59k
        uint8_t doStdev = getub(buf, off + 45) & 0x0f;
4710
        /* tracking stat
4711
         * bit 0 - prMes valid
4712
         * bit 1 - cpMes valid
4713
         * bit 2 - halfCycle valid
4714
         * bit 3 - halfCycle subtracted from phase
4715
         */
4716
2.59k
        uint8_t trkStat = getub(buf, off + 46);
4717
2.59k
        GPSD_LOG(LOG_PROG, &session->context->errout,
4718
2.59k
                 "UBX: RXM-RAWX: %u:%u:%u freqId %u prMes %f cpMes %f "
4719
2.59k
                 "doMes %f locktime %u\n"
4720
2.59k
                 "cno %u prStdev %u cpStdev %u doStdev %u rtkStat %u\n",
4721
2.59k
                 gnssId, svId, sigId, freqId, prMes, cpMes, doMes, locktime,
4722
2.59k
                 cno, prStdev, cpStdev, doStdev, trkStat);
4723
4724
2.59k
        session->gpsdata.raw.meas[i].gnssid = gnssId;
4725
2.59k
        session->gpsdata.raw.meas[i].sigid = sigId;
4726
4727
        /* some of these are GUESSES as the u-blox codes do not
4728
         * match RINEX codes */
4729
2.59k
        obs_code = sigid2obs(gnssId, sigId);
4730
4731
2.59k
        (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code,
4732
2.59k
                      sizeof(session->gpsdata.raw.meas[i].obs_code));
4733
4734
2.59k
        session->gpsdata.raw.meas[i].svid = svId;
4735
2.59k
        session->gpsdata.raw.meas[i].freqid = freqId;
4736
2.59k
        session->gpsdata.raw.meas[i].snr = cno;
4737
2.59k
        session->gpsdata.raw.meas[i].satstat = trkStat;
4738
2.59k
        if (trkStat & 1) {
4739
            // prMes valid
4740
1.17k
            session->gpsdata.raw.meas[i].pseudorange = prMes;
4741
1.41k
        } else {
4742
1.41k
            session->gpsdata.raw.meas[i].pseudorange = NAN;
4743
1.41k
        }
4744
2.59k
        if ((trkStat & 2) && (5 >= cpStdev)) {
4745
            // cpMes valid, RTKLIB uses 5 < cpStdev
4746
616
            session->gpsdata.raw.meas[i].carrierphase = cpMes;
4747
1.97k
        } else {
4748
1.97k
            session->gpsdata.raw.meas[i].carrierphase = NAN;
4749
1.97k
        }
4750
2.59k
        session->gpsdata.raw.meas[i].doppler = doMes;
4751
2.59k
        session->gpsdata.raw.meas[i].codephase = NAN;
4752
2.59k
        session->gpsdata.raw.meas[i].deltarange = NAN;
4753
2.59k
        session->gpsdata.raw.meas[i].locktime = locktime;
4754
2.59k
        if (0 == locktime) {
4755
            // possible slip
4756
221
            session->gpsdata.raw.meas[i].lli = 2;
4757
221
        }
4758
2.59k
    }
4759
4760
252
    return RAW_IS;
4761
411
}
4762
4763
/*
4764
 * Raw Subframes - UBX-RXM-SFRB
4765
 * In u-blox 7, only in raw firmware option
4766
 * Not in u-blox 8 or 9
4767
 */
4768
static gps_mask_t ubx_msg_rxm_sfrb(struct gps_device_t *session,
4769
                                   unsigned char *buf, size_t data_len)
4770
67
{
4771
67
    unsigned int i, chan, svid;
4772
67
    uint32_t words[10];
4773
4774
67
    if (42 > data_len) {
4775
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
4776
2
                 "UBX: RXM-SFRB: runt payload len %zd", data_len);
4777
2
        return 0;
4778
2
    }
4779
4780
65
    chan = (unsigned int)getub(buf, 0);
4781
65
    svid = (unsigned int)getub(buf, 1);
4782
65
    GPSD_LOG(LOG_PROG, &session->context->errout,
4783
65
             "UBX: RXM-SFRB: %u %u\n", chan, svid);
4784
4785
    // UBX does all the parity checking, but still bad data gets through
4786
715
    for (i = 0; i < 10; i++) {
4787
        // bits 24 to 31 undefined, remove them.
4788
650
        words[i] = (uint32_t)getleu32(buf, 4 * i + 2) & 0x00ffffff;
4789
650
    }
4790
4791
    // probably GPS, could be SBAS
4792
65
    return gpsd_interpret_subframe(session, GNSSID_GPS, svid, words);
4793
67
}
4794
4795
/*
4796
 * Raw Subframes - UBX-RXM-SFRBX
4797
 * in u-blox 8, protver 17 and up, time sync firmware only
4798
 * in u-blox F9P and HPG only
4799
 * in u-blox F10N, protVer 27 and up
4800
 * not present  before u-blox8
4801
 */
4802
static gps_mask_t ubx_msg_rxm_sfrbx(struct gps_device_t *session,
4803
                                    unsigned char *buf, size_t data_len)
4804
381
{
4805
381
    unsigned i;
4806
381
    uint32_t words[33];
4807
381
    char *chn_s;
4808
381
    gps_mask_t mask = 0;
4809
4810
381
    gnssid_t gnssId = getub(buf, 0);
4811
381
    unsigned svId = getub(buf, 1);
4812
    // reserved in Version 1, and some Version2.  Valid in protVer 27.31 and up
4813
381
    unsigned sigId = getub(buf, 2);
4814
381
    unsigned freqId = getub(buf, 3);
4815
381
    unsigned numWords = getub(buf, 4);
4816
381
    unsigned chn = getub(buf, 5);
4817
381
    unsigned version = getub(buf, 6);
4818
4819
381
    if (1 < version) {
4820
        // receiver channel in version 2 and up.
4821
        // valid range 0 to 13?
4822
142
        chn_s = "chn";
4823
239
    } else {
4824
239
        chn_s = "reserved";
4825
239
    }
4826
4827
381
    GPSD_LOG(LOG_PROG, &session->context->errout,
4828
381
             "UBX: RXM-SFRBX: version %u gnssId %u %s %u svId %u "
4829
381
             "sigId %u freqId %u words %u\n",
4830
381
             version, gnssId, chn_s, chn, svId, sigId, freqId, numWords);
4831
381
    GPSD_LOG(LOG_IO, &session->context->errout,
4832
381
             "UBX: RXM-SFRBX:   %s\n",
4833
381
             val2str((gnssId << 8) | sigId, vgnss_sig_ids));
4834
4835
381
    if (!IN(1, version, 2)) {
4836
        // unknown ersion
4837
7
        GPSD_LOG(LOG_WARN, &session->context->errout,
4838
7
                 "UBX: RXM-SFRBX: unknown version %u", version);
4839
7
        return 0;
4840
7
    }
4841
374
    if (data_len != (size_t)(8 + (4 * numWords)) ||
4842
364
        ROWS(words) < numWords) {
4843
        // test numwords directly to shut up Coverity
4844
10
        GPSD_LOG(LOG_WARN, &session->context->errout,
4845
10
                 "UBX: RXM-SFRBX: wrong payload len %zd, numwords %u "
4846
10
                 "s/b %u",
4847
10
                 data_len, 8 + (4 * numWords), numWords);
4848
10
        return 0;
4849
10
    }
4850
4851
364
    memset(words, 0, sizeof(words));
4852
3.43k
    for (i = 0; i < numWords; i++) {
4853
        // grab the words, don't mangle them
4854
3.07k
        words[i] = (uint32_t)getleu32(buf, 4 * i + 8);
4855
3.07k
    }
4856
4857
    // do we need freqId or chn?
4858
364
    mask = gpsd_interpret_subframe_raw(session, gnssId, sigId,
4859
364
                                       svId, words, numWords);
4860
364
    GPSD_LOG(LOG_IO, &session->context->errout,
4861
364
       "UBX: RXM-SFRBX: mask %s\n",
4862
364
       gps_maskdump(mask));
4863
364
    return mask;
4864
374
}
4865
4866
/**
4867
 * UBX-RXM-SPARTN -- Differential Correction Input Messages
4868
 *
4869
 * Present in ZED-F9P, HPG 1.50, protVer 27.50
4870
 */
4871
static gps_mask_t ubx_msg_rxm_spartn(struct gps_device_t *session,
4872
                                     unsigned char *buf,
4873
                                     size_t data_len UNUSED)
4874
3
{
4875
3
    unsigned version = getub(buf, 0);
4876
3
    unsigned flags = getub(buf, 1);
4877
3
    unsigned msgsubType = getleu16(buf, 2);
4878
3
    unsigned msgType = getleu16(buf, 6);
4879
4880
3
    if (1 != version) {
4881
2
        GPSD_LOG(LOG_WARN, &session->context->errout,
4882
2
                 "UBX: RXM-SPARTN, unknown version %u\n", version);
4883
2
        return 0;
4884
2
    }
4885
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
4886
1
             "UBX: RXM-SPARTN: version %u flags x%x msgType %u msgsubType %u\n",
4887
1
             version, flags, msgType, msgsubType);
4888
1
    GPSD_LOG(LOG_IO, &session->context->errout,
4889
1
            "UBX: RXM-SPARTN: flags (%s) msgType (%s) msgsubType (%s)\n",
4890
1
            val2str((flags >> 1) & 3, rxm_spart_flags),
4891
1
            val2str(msgType, spartn_mtypes),
4892
1
            val2str(msgsubType, spartn_mstypes));
4893
1
    return 0;
4894
3
}
4895
4896
/**
4897
 * SV Status Info
4898
 *
4899
 * May be good cycle ender
4900
 *
4901
 * Present in u-blox 7
4902
 */
4903
static gps_mask_t ubx_msg_rxm_svsi(struct gps_device_t *session,
4904
                                   unsigned char *buf, size_t data_len)
4905
71
{
4906
71
    unsigned numVis, numSV;
4907
4908
71
    if (8 > data_len) {
4909
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
4910
1
                 "UBX: RXM-SVSI: runt payload len %zd", data_len);
4911
1
        return 0;
4912
1
    }
4913
4914
70
    session->driver.ubx.iTOW = getleu32(buf, 0);
4915
70
    session->context->gps_week = getleu16(buf, 4);
4916
70
    numVis = getub(buf, 6);
4917
70
    numSV = getub(buf, 7);
4918
70
    GPSD_LOG(LOG_PROG, &session->context->errout,
4919
70
             "UBX: RXM-SVSI: iTOW=%lld week %d numVis %u numSV %u\n",
4920
70
             (long long)session->driver.ubx.iTOW,
4921
70
            session->context->gps_week, numVis, numSV);
4922
70
    return 0;
4923
71
}
4924
4925
/**
4926
 * Unique chip ID
4927
 * UBX-SEC-UNIQID
4928
 *
4929
 * grab the 5 byte serial number / chip id
4930
 */
4931
static gps_mask_t ubx_msg_sec_uniqid(struct gps_device_t *session,
4932
                                     unsigned char *buf,
4933
                                     size_t data_len)
4934
11
{
4935
11
    unsigned version;
4936
4937
11
    if (9 > data_len) {
4938
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
4939
3
                 "UBX: SEC-UNIQID: runt payload len %zd\n", data_len);
4940
3
        return 0;
4941
3
    }
4942
4943
8
    version = getub(buf, 0);
4944
8
    switch (version) {
4945
1
    case 1:
4946
        /* string of length 10 bytes
4947
         * PROTVER 18 -> 23 has five bytes of unique id.
4948
         * F10 is PROTVER 34, still has 5 bytes */
4949
1
        (void)snprintf(session->gpsdata.dev.sernum,
4950
1
                       sizeof(session->gpsdata.dev.sernum),
4951
1
                       "%02x%02x%02x%02x%02x",
4952
1
                       getub(buf, 4),
4953
1
                       getub(buf, 5),
4954
1
                       getub(buf, 6),
4955
1
                       getub(buf, 7),
4956
1
                       getub(buf, 8));
4957
1
        break;
4958
1
    case 2:
4959
        /* string of length 12 bytes
4960
         * some PROTVER 34 and beyond (for now) have six bytes of unique id.
4961
         * Such as MAX-M10S. */
4962
1
        (void)snprintf(session->gpsdata.dev.sernum,
4963
1
                       sizeof(session->gpsdata.dev.sernum),
4964
1
                       "%02x%02x%02x%02x%02x%02x",
4965
1
                       getub(buf, 4),
4966
1
                       getub(buf, 5),
4967
1
                       getub(buf, 6),
4968
1
                       getub(buf, 7),
4969
1
                       getub(buf, 8),
4970
1
                       getub(buf, 9));
4971
1
        break;
4972
6
    default:
4973
        // unknown version
4974
6
        GPSD_LOG(LOG_WARN, &session->context->errout,
4975
6
                 "UBX: SEC-UNIQID bad version\n");
4976
6
        return 0;
4977
8
    }
4978
4979
    // output chip id at LOG_INF
4980
2
    GPSD_LOG(LOG_INF, &session->context->errout,
4981
2
             "UBX: SEC-UNIQID: %s\n",
4982
2
             session->gpsdata.dev.sernum);
4983
2
    return 0;
4984
8
}
4985
4986
/**
4987
 * Survey-in data - UBX-TIM-SVIN
4988
 * Time Sync products only
4989
 */
4990
static gps_mask_t ubx_msg_tim_svin(struct gps_device_t *session,
4991
                                   unsigned char *buf, size_t data_len UNUSED)
4992
19
{
4993
19
    gps_mask_t mask = ONLINE_SET;
4994
19
    unsigned long dur = getleu32(buf, 0);
4995
19
    long meanX = getles32(buf, 4);
4996
19
    long meanY = getles32(buf, 8);
4997
19
    long meanZ = getles32(buf, 12);
4998
19
    unsigned long meanV = getleu32(buf, 16);
4999
19
    unsigned long obs = getleu32(buf, 20);
5000
19
    unsigned valid = getub(buf, 24);
5001
19
    unsigned active = getub(buf, 25);
5002
    // two reserved bytes
5003
5004
    // casts for 32 bit compatibility
5005
19
    GPSD_LOG(LOG_PROG, &session->context->errout,
5006
19
             "UBX: TIM-SVIN: dur=%lu meanX=%ld meanY=%ld meanZ=%ld meanV=%lu "
5007
19
             "obs=%lu valid=%u(%s) active=%u(%s)\n",
5008
19
             dur, meanX, meanY, meanZ, meanV, obs,
5009
19
             valid, val2str(valid, vsvin_valid),
5010
19
             active, val2str(active, vsvin_active));
5011
19
    return mask;
5012
19
}
5013
5014
/**
5015
 * Time Pulse Timedata - UBX-TIM-TP
5016
 */
5017
static gps_mask_t ubx_msg_tim_tp(struct gps_device_t *session,
5018
                                 unsigned char *buf, size_t data_len)
5019
4
{
5020
4
    gps_mask_t mask = ONLINE_SET;
5021
4
    uint32_t towMS;
5022
4
    uint32_t towSubMS;
5023
4
    uint64_t tow_tmp;    // temp to convert towSubMS to nano seconds.
5024
4
    int32_t qErr;
5025
4
    uint16_t week;
5026
4
    uint8_t flags;
5027
4
    uint8_t refInfo;
5028
4
    timespec_t ts_tow;
5029
4
    char buf2[80];
5030
4
    char buf3[80];
5031
4
    const char *warn_msg = "";
5032
5033
4
    static const struct flist_t tim_tp_flags[] = {
5034
4
        {0, 1, "timebase:GNSS"},
5035
4
        {1, 1, "timebase:UTC"},
5036
4
        {0, 2, "UTC:NA"},
5037
4
        {2, 2, "UTC:OK"},
5038
4
        {0, 0x0c, "RAIM:NA"},
5039
4
        {4, 0x0c, "RAIM:inactive"},
5040
4
        {8, 0x0c, "RAIM:active"},
5041
4
        {0x0c, 0x0c, "RAIM:Unk"},
5042
        // qErrValid  9-series, protVer 32 and up.
5043
4
        {0, 0x10, "qErr:Valid"},
5044
4
        {0x10, 0x10, "qErr:Invalid"},
5045
        // TpNotLocked, 9-series, protVer 32 and up.
5046
4
        {0, 0x20, "TP:Locked"},
5047
4
        {0x20, 0x20, "TP:Unlocked"},
5048
4
        {0, 0, NULL},
5049
4
    };
5050
5051
4
    static const struct flist_t tim_tp_refInfo[] = {
5052
4
        {0, 0x0f, "GNSS:GPS"},
5053
4
        {1, 0x0f, "GNSS:GLONASS"},
5054
4
        {2, 0x0f, "GNSS:BeiDou"},
5055
4
        {3, 0x0f, "GNSS:Galileo"},
5056
4
        {4, 0x0f, "GNSS:NavIc"},
5057
4
        {5, 0x0f, "GNSS:Unk5"},
5058
4
        {6, 0x0f, "GNSS:Unk6"},
5059
4
        {7, 0x0f, "GNSS:Unk7"},
5060
4
        {8, 0x0f, "GNSS:Unk8"},
5061
4
        {9, 0x0f, "GNSS:Unk9"},
5062
4
        {10, 0x0f, "GNSS:Unk10"},
5063
4
        {11, 0x0f, "GNSS:Unk11"},
5064
4
        {12, 0x0f, "GNSS:Unk12"},
5065
4
        {13, 0x0f, "GNSS:Unk13"},
5066
4
        {14, 0x0f, "GNSS:Unk14"},
5067
4
        {15, 0x0f, "GNSS:Unk"},
5068
4
        {0x00, 0xf0, "UTC:Unk"},
5069
4
        {0x10, 0xf0, "UTC:CRL"},
5070
4
        {0x20, 0xf0, "UTC:NIST"},
5071
4
        {0x30, 0xf0, "UTC:USNO"},
5072
4
        {0x40, 0xf0, "UTC:BIPM"},
5073
4
        {0x50, 0xf0, "UTC:EL"},
5074
4
        {0x60, 0xf0, "UTC:SU"},
5075
4
        {0x70, 0xf0, "UTC:NTSC"},
5076
4
        {0x80, 0xf0, "UTC:NPLI"},
5077
4
        {0x90, 0xf0, "UTC:Unk9"},
5078
4
        {0xa0, 0xf0, "UTC:Unk10"},
5079
4
        {0xb0, 0xf0, "UTC:Unk11"},
5080
4
        {0xc0, 0xf0, "UTC:Unk12"},
5081
4
        {0xd0, 0xf0, "UTC:Unk13"},
5082
4
        {0xe0, 0xf0, "UTC:Unk14"},
5083
4
        {0xf0, 0xf0, "UTC:Unk"},
5084
4
        {0, 0, NULL},
5085
4
    };
5086
5087
4
    if (16 > data_len) {
5088
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
5089
1
                 "UBX: TIM-TP: runt payload len %zd", data_len);
5090
1
        return 0;
5091
1
    }
5092
5093
3
    towMS = getleu32(buf, 0);
5094
    /* towSubMS is usually zero, but have seen 128, and 4294967168.
5095
     * towSubMs == 1 is 233 femto seconds!
5096
     * towSubMS == 128 is 29.802 pico seconds!
5097
     * towSubMS == 4294967168 is 0.9999999701976775 milli seconds
5098
     */
5099
3
    towSubMS = getleu32(buf, 4);
5100
3
    qErr = getles32(buf, 8);
5101
3
    week = getleu16(buf, 12);
5102
3
    flags = buf[14];
5103
3
    refInfo = buf[15];
5104
5105
3
    MSTOTS(&ts_tow, towMS);
5106
    /* scale towSubMS to nano seconds, add in 500 pico seconds for rounding
5107
     * then remove the u-blox scaling. */
5108
3
    tow_tmp = (((uint64_t)towSubMS * 1000000UL) + 500000UL) >> 32;
5109
3
    ts_tow.tv_nsec += tow_tmp;
5110
3
    TS_NORM(&ts_tow);       // can happen on rounding 0.999999999 to 1.0
5111
5112
    // check that it is close to top of second??
5113
3
    if (3 != (flags & 0x03)) {
5114
1
        warn_msg = " Not locked to UTC";
5115
2
    } else {
5116
        // are we UTC, and towSubMs is zer
5117
5118
        // leap already added!?!?
5119
2
        int saved_leap = session->context->leap_seconds;
5120
        // remove it!
5121
2
        session->context->leap_seconds = 0;
5122
5123
        // good, save qErr and qErr_time
5124
2
        session->gpsdata.qErr = qErr;
5125
        // FIXME?  save as ftow??
5126
2
        session->gpsdata.qErr_time = gpsd_gpstime_resolv(session, week, ts_tow);
5127
5128
        // restore leap
5129
2
        session->context->leap_seconds = saved_leap;
5130
2
    }
5131
5132
    // casts for 32 bit compatibility
5133
3
    GPSD_LOG(LOG_PROG, &session->context->errout,
5134
3
             "UBX: TIM-TP: towMS %lu, towSubMS %ld, qErr %ld week %u "
5135
3
             "flags x%02x, refInfo x%02x\n",
5136
3
             (unsigned long)towMS, (long)towSubMS, (long)qErr,
5137
3
              week, flags, refInfo);
5138
3
    GPSD_LOG(LOG_IO, &session->context->errout,
5139
3
             "UBX: TIM-TP: flags (%s) refInfo (%s) tos_tmp %llu %s\n",
5140
3
             flags2str(flags, tim_tp_flags, buf2, sizeof(buf2)),
5141
3
             flags2str(refInfo, tim_tp_refInfo, buf3, sizeof(buf3)),
5142
3
             (long long unsigned)tow_tmp, warn_msg);
5143
5144
3
    return mask;
5145
4
}
5146
5147
static gps_mask_t ubx_parse(struct gps_device_t * session, unsigned char *buf,
5148
                            size_t len)
5149
19.1k
{
5150
19.1k
    size_t data_len;
5151
19.1k
    unsigned short msgid;
5152
19.1k
    gps_mask_t mask = 0;
5153
19.1k
    unsigned char min_protver = 0;
5154
5155
    // the packet at least contains a head long enough for an empty message
5156
19.1k
    if (UBX_PREFIX_LEN > len) {
5157
0
        return 0;
5158
0
    }
5159
5160
19.1k
    session->cycle_end_reliable = true;
5161
19.1k
    session->driver.ubx.iTOW = -1;        // set by decoder
5162
5163
    // extract message id and length
5164
19.1k
    msgid = getbes16(buf, 2);
5165
19.1k
    data_len = (size_t) getles16(buf, 4);
5166
5167
    /* FIXME: make each case just call one function.
5168
     / then this switch can be turned into a table. */
5169
19.1k
    switch (msgid) {
5170
1
    case UBX_ACK_ACK:
5171
1
        FALLTHROUGH
5172
67
    case UBX_ACK_NAK:
5173
67
        mask = ubx_msg_ack(session, buf, data_len);
5174
67
        break;
5175
5176
    /* UBX-AID-*
5177
     * removed in protVer 32 */
5178
5
    case UBX_CFG_DOSC:
5179
5
        mask = ubx_msg_cfg_dosc(session, &buf[UBX_PREFIX_LEN], data_len);
5180
5
        break;
5181
11
    case UBX_CFG_ESRC:
5182
11
        mask = ubx_msg_cfg_esrc(session, &buf[UBX_PREFIX_LEN], data_len);
5183
11
        break;
5184
49
    case UBX_CFG_NAV5:
5185
        // deprecated in u-blox 10
5186
49
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: CFG-NAV5\n");
5187
49
        break;
5188
37
    case UBX_CFG_NAVX5:
5189
        // deprecated in u-blox 10
5190
37
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: CFG-NAVX5\n");
5191
37
        break;
5192
64
    case UBX_CFG_PRT:
5193
        // deprecated in u-blox 10
5194
64
        if (session->driver.ubx.port_id != buf[UBX_PREFIX_LEN + 0] ) {
5195
25
            session->driver.ubx.port_id = buf[UBX_PREFIX_LEN + 0];
5196
25
            GPSD_LOG(LOG_INF, &session->context->errout,
5197
25
                     "UBX: CFG-PRT: port %d\n", session->driver.ubx.port_id);
5198
25
        }
5199
64
        break;
5200
7
    case UBX_CFG_RATE:
5201
        // deprecated in u-blox 10
5202
7
        mask = ubx_msg_cfg_rate(session, &buf[UBX_PREFIX_LEN], data_len);
5203
7
        break;
5204
5
    case UBX_CFG_VALGET:
5205
5
        min_protver = 24;
5206
5
        mask = ubx_msg_cfg_valget(session, &buf[UBX_PREFIX_LEN], data_len);
5207
5
        break;
5208
5209
6
    case UBX_ESF_ALG:
5210
6
        mask = ubx_msg_esf_alg(session, &buf[UBX_PREFIX_LEN], data_len);
5211
6
        break;
5212
9
    case UBX_ESF_INS:
5213
9
        mask = ubx_msg_esf_ins(session, &buf[UBX_PREFIX_LEN], data_len);
5214
9
        break;
5215
98
    case UBX_ESF_MEAS:
5216
98
        mask = ubx_msg_esf_meas(session, &buf[UBX_PREFIX_LEN], data_len);
5217
98
        break;
5218
189
    case UBX_ESF_RAW:
5219
189
        mask = ubx_msg_esf_raw(session, &buf[UBX_PREFIX_LEN], data_len);
5220
189
        break;
5221
151
    case UBX_ESF_STATUS:
5222
151
        mask = ubx_msg_esf_status(session, &buf[UBX_PREFIX_LEN], data_len);
5223
151
        break;
5224
5225
13
    case UBX_HNR_ATT:
5226
13
        min_protver = 19;       // actually 19.2
5227
13
        mask = ubx_msg_hnr_att(session, &buf[UBX_PREFIX_LEN], data_len);
5228
13
        break;
5229
8
    case UBX_HNR_INS:
5230
8
        min_protver = 19;       // actually 19.1
5231
8
        mask = ubx_msg_hnr_ins(session, &buf[UBX_PREFIX_LEN], data_len);
5232
8
        break;
5233
184
    case UBX_HNR_PVT:
5234
184
        min_protver = 19;
5235
184
        mask = ubx_msg_hnr_pvt(session, &buf[UBX_PREFIX_LEN], data_len);
5236
184
        break;
5237
5238
9
    case UBX_INF_DEBUG:
5239
9
        FALLTHROUGH
5240
112
    case UBX_INF_ERROR:
5241
112
        FALLTHROUGH
5242
123
    case UBX_INF_NOTICE:
5243
123
        FALLTHROUGH
5244
124
    case UBX_INF_TEST:
5245
124
        FALLTHROUGH
5246
125
    case UBX_INF_USER:
5247
125
        FALLTHROUGH
5248
241
    case UBX_INF_WARNING:
5249
241
        min_protver = 13;
5250
241
        mask = ubx_msg_inf(session, buf, data_len);
5251
241
        break;
5252
5253
159
    case UBX_LOG_BATCH:
5254
159
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: LOG-BATCH\n");
5255
159
        mask = ubx_msg_log_batch(session, &buf[UBX_PREFIX_LEN], data_len);
5256
159
        break;
5257
16
    case UBX_LOG_INFO:
5258
16
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: LOG-INFO\n");
5259
16
        mask = ubx_msg_log_info(session, &buf[UBX_PREFIX_LEN], data_len);
5260
16
        break;
5261
20
    case UBX_LOG_RETRIEVEPOS:
5262
20
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: LOG-RETRIEVEPOS\n");
5263
20
        mask = ubx_msg_log_retrievepos(session, &buf[UBX_PREFIX_LEN], data_len);
5264
20
        break;
5265
14
    case UBX_LOG_RETRIEVEPOSEXTRA:
5266
14
        GPSD_LOG(LOG_PROG, &session->context->errout,
5267
14
                 "UBX: LOG-RETRIEVEPOSEXTRA\n");
5268
14
        mask = ubx_msg_log_retrieveposextra(session, &buf[UBX_PREFIX_LEN],
5269
14
                                            data_len);
5270
14
        break;
5271
20
    case UBX_LOG_RETRIEVESTRING:
5272
20
        GPSD_LOG(LOG_PROG, &session->context->errout,
5273
20
                 "UBX: LOG-RETRIEVESTRING\n");
5274
20
        mask = ubx_msg_log_retrievestring(session, &buf[UBX_PREFIX_LEN],
5275
20
                                          data_len);
5276
20
        break;
5277
5278
1
    case UBX_MON_BATCH:
5279
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-BATCH\n");
5280
1
        break;
5281
32
    case UBX_MON_COMMS:
5282
32
        mask = ubx_msg_mon_comms(session, &buf[UBX_PREFIX_LEN], data_len);
5283
32
        break;
5284
11
    case UBX_MON_EXCEPT:
5285
11
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-EXCEPT\n");
5286
11
        break;
5287
1
    case UBX_MON_GNSS:
5288
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-GNSS\n");
5289
1
        break;
5290
13
    case UBX_MON_HW:
5291
13
        min_protver = 12;
5292
13
        mask = ubx_msg_mon_hw(session, &buf[UBX_PREFIX_LEN], data_len);
5293
13
        break;
5294
1
    case UBX_MON_HW2:
5295
         // Deprecated in protVer 32 (9-series, 10-series)
5296
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-HW2\n");
5297
1
        break;
5298
1
    case UBX_MON_HW3:
5299
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-HW3\n");
5300
1
        break;
5301
1
    case UBX_MON_IO:
5302
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-IO\n");
5303
1
        break;
5304
1
    case UBX_MON_IPC:
5305
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-IPC\n");
5306
1
        break;
5307
1
    case UBX_MON_MSGPP:
5308
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-MSGPP\n");
5309
1
        break;
5310
1
    case UBX_MON_PATCH:
5311
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-PATCH\n");
5312
1
        break;
5313
59
    case UBX_MON_RF:
5314
59
        mask = ubx_msg_mon_rf(session, &buf[UBX_PREFIX_LEN], data_len);
5315
59
        break;
5316
2
    case UBX_MON_RXBUF:
5317
2
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-RXBUF\n");
5318
2
        mask = ubx_msg_mon_rxbuf(session, &buf[UBX_PREFIX_LEN], data_len);
5319
2
        break;
5320
12
    case UBX_MON_RXR:
5321
12
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-RXR\n");
5322
12
        break;
5323
1
    case UBX_MON_SCHED:
5324
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-SCHED\n");
5325
1
        break;
5326
2
    case UBX_MON_SMGR:
5327
2
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-SMGR\n");
5328
2
        break;
5329
1
    case UBX_MON_SPAN:
5330
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-SPAN\n");
5331
1
        break;
5332
2
    case UBX_MON_TXBUF:
5333
2
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-TXBUF\n");
5334
2
        mask = ubx_msg_mon_txbuf(session, &buf[UBX_PREFIX_LEN], data_len);
5335
2
        break;
5336
35
    case UBX_MON_USB:
5337
35
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MON-USB\n");
5338
35
        break;
5339
94
    case UBX_MON_VER:
5340
94
        mask = ubx_msg_mon_ver(session, &buf[UBX_PREFIX_LEN], data_len);
5341
94
        break;
5342
5343
69
    case UBX_NAV_AOPSTATUS:
5344
69
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-AOPSTATUS\n");
5345
69
        break;
5346
110
    case UBX_NAV_ATT:
5347
110
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-ATT\n");
5348
110
        break;
5349
20
    case UBX_NAV_CLOCK:
5350
20
        mask = ubx_msg_nav_clock(session, &buf[UBX_PREFIX_LEN], data_len);
5351
20
        break;
5352
65
    case UBX_NAV_DGPS:
5353
65
        mask = ubx_msg_nav_dgps(session, &buf[UBX_PREFIX_LEN], data_len);
5354
65
        break;
5355
591
    case UBX_NAV_DOP:
5356
        // DOP seems to be the last NAV sent in a cycle, unless NAV-EOE
5357
591
        mask = ubx_msg_nav_dop(session, &buf[UBX_PREFIX_LEN], data_len);
5358
591
        break;
5359
54
    case UBX_NAV_EELL:
5360
54
        min_protver = 18;
5361
54
        mask = ubx_msg_nav_eell(session, &buf[UBX_PREFIX_LEN], data_len);
5362
54
        break;
5363
35
    case UBX_NAV_EKFSTATUS:
5364
35
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-EKFSTATUS\n");
5365
35
        break;
5366
24
    case UBX_NAV_EOE:
5367
24
        min_protver = 18;
5368
24
        mask = ubx_msg_nav_eoe(session, &buf[UBX_PREFIX_LEN], data_len);
5369
24
        break;
5370
67
    case UBX_NAV_GEOFENCE:
5371
67
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-GEOFENCE\n");
5372
67
        break;
5373
119
    case UBX_NAV_HPPOSECEF:
5374
119
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-HPPOSECEF\n");
5375
119
        if (28 > data_len) {
5376
79
            GPSD_LOG(LOG_WARN, &session->context->errout,
5377
79
                     "UBX: NAV-HPPOSECEF: runt payload len %zd", data_len);
5378
79
        } else {
5379
40
            mask = ubx_msg_nav_hpposecef(session, &buf[UBX_PREFIX_LEN],
5380
40
                                         data_len);
5381
40
        }
5382
119
        break;
5383
42
    case UBX_NAV_HPPOSLLH:
5384
42
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-HPPOSLLH\n");
5385
42
        mask = ubx_msg_nav_hpposllh(session, &buf[UBX_PREFIX_LEN], data_len);
5386
42
        break;
5387
68
    case UBX_NAV_ODO:
5388
68
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-ODO\n");
5389
68
        break;
5390
32
    case UBX_NAV_ORB:
5391
32
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-ORB\n");
5392
32
        break;
5393
115
    case UBX_NAV_POSECEF:
5394
115
        mask = ubx_msg_nav_posecef(session, &buf[UBX_PREFIX_LEN], data_len);
5395
115
        break;
5396
183
    case UBX_NAV_POSLLH:
5397
183
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-POSLLH\n");
5398
183
        mask = ubx_msg_nav_posllh(session, &buf[UBX_PREFIX_LEN], data_len);
5399
183
        break;
5400
68
    case UBX_NAV_POSUTM:
5401
68
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-POSUTM\n");
5402
68
        break;
5403
288
    case UBX_NAV_PVAT:
5404
288
        min_protver = 30;
5405
288
        if (116 > data_len) {
5406
74
            GPSD_LOG(LOG_WARN, &session->context->errout,
5407
74
                     "UBX: NAV-PVAT: runt payload len %zd", data_len);
5408
214
        } else {
5409
214
            mask = ubx_msg_nav_pvat(session, &buf[UBX_PREFIX_LEN], data_len);
5410
214
        }
5411
288
        break;
5412
676
    case UBX_NAV_PVT:
5413
676
        min_protver = 14;
5414
        // u-blox 6 and 7 are 84 bytes, u-blox 8 and 9 are 92 bytes
5415
676
        if (84 > data_len) {
5416
136
            GPSD_LOG(LOG_WARN, &session->context->errout,
5417
136
                     "UBX: NAV-PVT: runt payload len %zd", data_len);
5418
540
        } else {
5419
540
            mask = ubx_msg_nav_pvt(session, &buf[UBX_PREFIX_LEN], data_len);
5420
540
        }
5421
676
        break;
5422
134
    case UBX_NAV_RELPOSNED:
5423
134
        min_protver = 20;
5424
134
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-RELPOSNED\n");
5425
134
        mask = ubx_msg_nav_relposned(session, &buf[UBX_PREFIX_LEN], data_len);
5426
134
        break;
5427
70
    case UBX_NAV_RESETODO:
5428
70
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-RESETODO\n");
5429
70
        break;
5430
501
    case UBX_NAV_SAT:
5431
501
        min_protver = 15;
5432
501
        mask = ubx_msg_nav_sat(session, &buf[UBX_PREFIX_LEN], data_len);
5433
501
        break;
5434
944
    case UBX_NAV_SBAS:
5435
944
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-SBAS\n");
5436
944
        mask = ubx_msg_nav_sbas(session, &buf[UBX_PREFIX_LEN], data_len);
5437
944
        break;
5438
513
    case UBX_NAV_SIG:
5439
513
        min_protver = 27;
5440
513
        mask = ubx_msg_nav_sig(session, &buf[UBX_PREFIX_LEN], data_len);
5441
513
        break;
5442
509
    case UBX_NAV_SOL:
5443
        /* UBX-NAV-SOL deprecated in u-blox 6,
5444
         * removed in protVer 32 (9 and 10 series).
5445
         * Use UBX-NAV-PVT instead */
5446
509
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-SOL\n");
5447
509
        mask = ubx_msg_nav_sol(session, &buf[UBX_PREFIX_LEN], data_len);
5448
509
        break;
5449
841
    case UBX_NAV_STATUS:
5450
841
        mask = ubx_msg_nav_status(session, &buf[UBX_PREFIX_LEN], data_len);
5451
841
        break;
5452
4
    case UBX_NAV_SVIN:
5453
4
        if (40 > data_len) {
5454
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
5455
1
                     "UBX: NAV-SVIN: runt payload len %zd", data_len);
5456
1
            break;
5457
1
        }
5458
3
        mask = ubx_msg_nav_svin(session, &buf[UBX_PREFIX_LEN], data_len);
5459
3
        break;
5460
650
    case UBX_NAV_SVINFO:
5461
650
        mask = ubx_msg_nav_svinfo(session, &buf[UBX_PREFIX_LEN], data_len);
5462
650
        break;
5463
69
    case UBX_NAV_TIMEBDS:
5464
69
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-TIMEBDS\n");
5465
69
        break;
5466
83
    case UBX_NAV_TIMEGAL:
5467
83
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-TIMEGAL\n");
5468
83
        break;
5469
67
    case UBX_NAV_TIMEGLO:
5470
67
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-TIMEGLO\n");
5471
67
        break;
5472
635
    case UBX_NAV_TIMEGPS:
5473
635
        mask = ubx_msg_nav_timegps(session, &buf[UBX_PREFIX_LEN], data_len);
5474
635
        break;
5475
294
    case UBX_NAV_TIMELS:
5476
294
        mask = ubx_msg_nav_timels(session, &buf[UBX_PREFIX_LEN], data_len);
5477
294
        break;
5478
62
    case UBX_NAV_TIMEQZSS:
5479
62
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-TIMEQZSS\n");
5480
62
        break;
5481
525
    case UBX_NAV_TIMEUTC:
5482
525
        mask = ubx_msg_nav_timeutc(session, &buf[UBX_PREFIX_LEN], data_len);
5483
525
        break;
5484
113
    case UBX_NAV_VELECEF:
5485
113
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-VELECEF\n");
5486
113
        mask = ubx_msg_nav_velecef(session, &buf[UBX_PREFIX_LEN], data_len);
5487
113
        break;
5488
129
    case UBX_NAV_VELNED:
5489
129
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: NAV-VELNED\n");
5490
129
        mask = ubx_msg_nav_velned(session, &buf[UBX_PREFIX_LEN], data_len);
5491
129
        break;
5492
5493
1
    case UBX_MGA_ACK:
5494
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MGA-ACK\n");
5495
1
        break;
5496
1
    case UBX_MGA_DBD:
5497
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: MGA-DBD\n");
5498
1
        break;
5499
5500
67
    case UBX_RXM_ALM:
5501
67
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-ALM\n");
5502
67
        break;
5503
6
    case UBX_RXM_COR:
5504
6
        min_protver = 27;
5505
6
        if (12 > data_len) {
5506
4
            GPSD_LOG(LOG_WARN, &session->context->errout,
5507
4
                     "UBX: RXM-COR: runt payload len %zd", data_len);
5508
4
            return 0;
5509
4
        }
5510
2
        mask = ubx_msg_rxm_cor(session, &buf[UBX_PREFIX_LEN], data_len);
5511
2
        break;
5512
41
    case UBX_RXM_EPH:
5513
41
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-EPH\n");
5514
41
        break;
5515
1
    case UBX_RXM_IMES:
5516
        // Removed in protVer 32 (9-series)
5517
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-IMES\n");
5518
1
        break;
5519
1
    case UBX_RXM_MEASX:
5520
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-MEASX\n");
5521
1
        break;
5522
1
    case UBX_RXM_PMREQ:
5523
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-PMREQ\n");
5524
1
        break;
5525
1
    case UBX_RXM_POSREQ:
5526
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-POSREQ\n");
5527
1
        break;
5528
42
    case UBX_RXM_RAW:
5529
42
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-RAW\n");
5530
42
        break;
5531
537
    case UBX_RXM_RAWX:
5532
537
        mask = ubx_msg_rxm_rawx(session, &buf[UBX_PREFIX_LEN], data_len);
5533
537
        break;
5534
1
    case UBX_RXM_RLM:
5535
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-RLM\n");
5536
1
        break;
5537
86
    case UBX_RXM_RTCM:
5538
86
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: RXM-RTCM\n");
5539
86
        break;
5540
67
    case UBX_RXM_SFRB:
5541
67
        mask = ubx_msg_rxm_sfrb(session, &buf[UBX_PREFIX_LEN], data_len);
5542
67
        break;
5543
385
    case UBX_RXM_SFRBX:
5544
385
        min_protver = 17;
5545
385
        if (8 > data_len) {
5546
4
            GPSD_LOG(LOG_WARN, &session->context->errout,
5547
4
                     "UBX: RXM-SFRBX: runt payload len %zd", data_len);
5548
4
            break;
5549
4
        }
5550
381
        mask = ubx_msg_rxm_sfrbx(session, &buf[UBX_PREFIX_LEN], data_len);
5551
381
        break;
5552
4
    case UBX_RXM_SPARTN:
5553
4
        min_protver = 27;
5554
4
        if (8 > data_len) {
5555
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
5556
1
                     "UBX: RXM-SPARTN: runt payload len %zd", data_len);
5557
1
            return 0;
5558
1
        }
5559
3
        mask = ubx_msg_rxm_spartn(session, &buf[UBX_PREFIX_LEN], data_len);
5560
3
        break;
5561
71
    case UBX_RXM_SVSI:
5562
        // Removed in protVer 32 (9-series)
5563
        // Use UBX-NAV-ORB instead
5564
71
        mask = ubx_msg_rxm_svsi(session, &buf[UBX_PREFIX_LEN], data_len);
5565
71
        break;
5566
5567
    // undocumented
5568
    // case UBX_SEC_SESSID:
5569
    //     GPSD_LOG(LOG_PROG, &session->context->errout, "UBX-SEC-SESSID\n");
5570
    //     break;
5571
1
    case UBX_SEC_SIGN:
5572
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: SEC_SIGN\n");
5573
1
        break;
5574
11
    case UBX_SEC_UNIQID:
5575
11
        mask = ubx_msg_sec_uniqid(session, &buf[UBX_PREFIX_LEN], data_len);
5576
11
        break;
5577
1
    case UBX_TIM_DOSC:
5578
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-DOSC\n");
5579
1
        break;
5580
1
    case UBX_TIM_FCHG:
5581
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-FCHG\n");
5582
1
        break;
5583
1
    case UBX_TIM_HOC:
5584
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-HOC\n");
5585
1
        break;
5586
1
    case UBX_TIM_SMEAS:
5587
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-SMEAS\n");
5588
1
        break;
5589
20
    case UBX_TIM_SVIN:
5590
20
        if (28 > data_len) {
5591
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
5592
1
                     "UBX: TIM-SVIN: runt payload len %zd", data_len);
5593
1
            break;
5594
1
        }
5595
19
        mask = ubx_msg_tim_svin(session, &buf[UBX_PREFIX_LEN], data_len);
5596
19
        break;
5597
1
    case UBX_TIM_TM:
5598
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-TM\n");
5599
1
        break;
5600
1
    case UBX_TIM_TM2:
5601
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-TM2\n");
5602
1
        break;
5603
4
    case UBX_TIM_TP:
5604
4
        mask = ubx_msg_tim_tp(session, &buf[UBX_PREFIX_LEN], data_len);
5605
4
        break;
5606
1
    case UBX_TIM_TOS:
5607
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-TOS\n");
5608
1
        break;
5609
1
    case UBX_TIM_VCOCAL:
5610
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-VCOCAL\n");
5611
1
        break;
5612
1
    case UBX_TIM_VRFY:
5613
1
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX: TIM-VRFY\n");
5614
1
        break;
5615
5616
7.42k
    default:
5617
7.42k
        GPSD_LOG(LOG_WARN, &session->context->errout,
5618
19.1k
                 "UBX: unknown packet id x%04hx (length %zd)\n",
5619
19.1k
                 msgid, len);
5620
19.1k
    }
5621
#ifdef __UNUSED
5622
    // debug
5623
    GPSD_LOG(LOG_PROG, &session->context->errout,
5624
             "UBX: msgid x%04x end x%04x last x%04x iTOW %lld last %lld\n",
5625
             msgid,
5626
             session->driver.ubx.end_msgid,
5627
             session->driver.ubx.last_msgid,
5628
             (long long)session->driver.ubx.iTOW,
5629
             (long long)session->driver.ubx.last_iTOW);
5630
#endif
5631
5632
    // iTOW drives the cycle start/end detection
5633
    // iTOW is in ms, can go forward or backward
5634
19.1k
    if (-1 < session->driver.ubx.iTOW) {
5635
6.64k
        int64_t iTOW_diff;
5636
5637
        // this sentence has a (maybe good) time
5638
        // end of cycle ?
5639
6.64k
        if (session->driver.ubx.end_msgid == msgid) {
5640
            // got known cycle ender.  Assume end of cycle, report it
5641
273
            GPSD_LOG(LOG_PROG, &session->context->errout,
5642
273
                     "UBX: cycle end x%04x iTOW %lld\n",
5643
273
                     msgid, (long long)session->driver.ubx.iTOW);
5644
273
            mask |= REPORT_IS;
5645
273
        }
5646
5647
        // start of cycle?  Start can equal end if only one message per epoch
5648
        // u-blox iTOW can have ms jitter in the same epoch!
5649
6.64k
        iTOW_diff = session->driver.ubx.last_iTOW - session->driver.ubx.iTOW;
5650
6.64k
        if (10 < llabs(iTOW_diff)) {
5651
            // time changed more than 10 ms (100 Hz), cycle start
5652
5653
2.41k
            if (session->driver.ubx.end_msgid !=
5654
2.41k
                session->driver.ubx.last_msgid) {
5655
                // new cycle ender
5656
143
                GPSD_LOG(LOG_PROG, &session->context->errout,
5657
143
                         "UBX: new ender x%04x was x%04x iTOW %lld was %lld\n",
5658
143
                         session->driver.ubx.last_msgid,
5659
143
                         session->driver.ubx.end_msgid,
5660
143
                         (long long)session->driver.ubx.iTOW,
5661
143
                         (long long)session->driver.ubx.last_iTOW);
5662
143
                session->driver.ubx.end_msgid = session->driver.ubx.last_msgid;
5663
143
            }
5664
2.41k
            session->driver.ubx.last_iTOW = session->driver.ubx.iTOW;
5665
2.41k
            mask |= CLEAR_IS;
5666
2.41k
        }
5667
5668
6.64k
        session->driver.ubx.last_msgid = msgid;
5669
        // FIXME: last_time never used...
5670
6.64k
        session->driver.ubx.last_time = session->newdata.time;
5671
12.5k
    } else {
5672
        // no time
5673
        /* debug
5674
        GPSD_LOG(LOG_ERROR, &session->context->errout,
5675
                 "UBX: No time, msgid %x\n", msgid);
5676
         */
5677
12.5k
    }
5678
5679
    // Did protver change?
5680
19.1k
    if (min_protver > session->driver.ubx.protver) {
5681
        // this GPS is at least min_protver
5682
1.75k
        session->driver.ubx.protver = min_protver;
5683
1.75k
    }
5684
19.1k
    if (session->driver.ubx.last_protver != session->driver.ubx.protver) {
5685
        /* Assumption: we just did init, but did not have
5686
         * protver then, so init is not complete.  Finish now.
5687
         * unless user requested passive mode */
5688
2.01k
        if (session->mode == O_OPTIMIZE &&
5689
0
            !session->context->passive) {
5690
0
            ubx_mode(session, MODE_BINARY);
5691
0
        }
5692
2.01k
        GPSD_LOG(LOG_PROG, &session->context->errout,
5693
2.01k
                 "UBX: new PROTVER %u was %u\n",
5694
2.01k
                 session->driver.ubx.protver,
5695
2.01k
                 session->driver.ubx.last_protver);
5696
2.01k
        session->driver.ubx.last_protver = session->driver.ubx.protver;
5697
        // restart init queue
5698
2.01k
        session->queue = 0;
5699
2.01k
    }
5700
5701
19.1k
    if (!session->context->readonly &&
5702
13.1k
        0 <= session->queue &&
5703
13.1k
        100 > session->queue &&
5704
12.6k
        0 < session->driver.ubx.protver) {
5705
6.46k
        unsigned char msg[4] = {0};
5706
5707
6.46k
        GPSD_LOG(LOG_DATA, &session->context->errout,
5708
6.46k
                 "UBX: queue %d\n", session->queue);
5709
5710
        /* handle the init queue.  Some u-blox parts get cranky when they
5711
         * get too many configuration changes at once.
5712
         */
5713
5714
6.46k
        if (50 <= session->queue &&
5715
999
            !session->context->passive) {
5716
            // turn off common NMEA, every 3rd queue turn.
5717
718
            int i = session->queue - 50;
5718
718
            if (0 == (i % 3)) {
5719
252
                int j = i / 3;
5720
252
                if (j < (int)sizeof(nmea_off)) {
5721
190
                    msg[0] = 0xf0;          // class, NMEA
5722
190
                    msg[2] = 0x00;          // rate, off
5723
190
                    msg[1] = nmea_off[j];    // msg id to turn off
5724
190
                    (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5725
190
                }
5726
252
            }
5727
718
        }
5728
5729
6.46k
        switch (session->queue) {
5730
1.07k
        case 0:
5731
            /* need to doi this right away, so there are UBX message
5732
            * to push this queue forward */
5733
1.07k
            if (!session->context->passive) {
5734
                // turn on common UBX-NAV
5735
671
                unsigned i;
5736
5737
671
                msg[0] = 0x01;          // class, UBX-NAV
5738
671
                msg[2] = 0x01;          // rate, one
5739
2.68k
                for (i = 0; i < ROWS(ubx_nav_on); i++) {
5740
2.01k
                    msg[1] = ubx_nav_on[i];          // msg id to turn on
5741
2.01k
                    (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5742
2.01k
                }
5743
671
            }
5744
1.07k
            break;
5745
140
        case 10:
5746
            /* Older u-blox (6-series) may have ignored earlier requests
5747
             * for UBX-MON-VER.  Try again if needed. */
5748
140
            if ('\0' == session->subtype[0]) {
5749
                // request UBX-MON-VER, for  SW and HW Versions
5750
12
                (void)ubx_write(session, UBX_CLASS_MON, 0x04, NULL, 0);
5751
12
            }
5752
140
            break;
5753
87
        case 20:
5754
87
            if (!session->context->passive) {
5755
57
                unsigned i;
5756
5757
57
                msg[0] = 0x01;          // class, UBX-NAV
5758
57
                msg[2] = 0x01;          // rate, one
5759
57
                if (15 > session->driver.ubx.protver) {
5760
                    /* protver 14 or less, or unknown version,
5761
                     * We should have a version now.
5762
                     * Turn on pre-15 UBX-NAV stuff */
5763
5764
96
                    for (i = 0; i < ROWS(ubx_14_nav_on); i++) {
5765
                        // msg id to turn on
5766
64
                        msg[1] = ubx_14_nav_on[i];
5767
64
                        (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5768
64
                    }
5769
32
                } else {
5770
                    /* must be 15 <= session->driver.ubx.protver
5771
                     * turn on 15+ UBX-NAV */
5772
150
                    for (i = 0; i < ROWS(ubx_15_nav_on); i++) {
5773
                        // msg id to turn on
5774
125
                        msg[1] = ubx_15_nav_on[i];
5775
125
                        (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5776
125
                    }
5777
25
                }
5778
57
            }
5779
87
            break;
5780
23
        case 71:
5781
23
            if (!session->context->passive &&
5782
17
                15 <= session->driver.ubx.protver) {
5783
                // good cycle ender, except when it is not the ender...
5784
9
                msg[0] = 0x01;          // class
5785
9
                msg[1] = 0x61;          // msg id  = UBX-NAV-EOE
5786
9
                msg[2] = 0x01;          // every cycle
5787
9
                (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5788
9
            }
5789
23
            break;
5790
21
        case 75:
5791
21
            if (!session->context->passive &&
5792
16
                15 <= session->driver.ubx.protver) {
5793
9
                msg[0] = 0x01;          // class
5794
9
                msg[1] = 0x26;          // msg id  = UBX-NAV-TIMELS
5795
9
                msg[2] = 0xff;          // about every 4 mins if nav rate is 1Hz
5796
9
                (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5797
9
            }
5798
21
            break;
5799
16
        case 80:
5800
16
            if (18 <= session->driver.ubx.protver) {
5801
                // No UNIQ-ID before PROTVER 18
5802
                // UBX-SEC-UNIQID: query for uniq id
5803
6
                (void)ubx_write(session, UBX_CLASS_SEC, 0x03, NULL, 0);
5804
6
            }
5805
16
            break;
5806
13
        case 83:
5807
13
            if (session->context->passive) {
5808
                // do nothing
5809
10
            } else if (27 > session->driver.ubx.protver) {
5810
6
                msg[0] = 0x0a;          // class, UBX-MON
5811
6
                msg[1] = 0x09;          // MON-HW
5812
6
                msg[2] = 0x04;          // every 4
5813
6
                (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5814
6
            } else {
5815
4
                msg[0] = 0x0a;          // class, UBX-MON
5816
4
                msg[1] = 0x38;          // MON-RF
5817
4
                msg[2] = 0x04;          // every 4
5818
4
                (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5819
4
            }
5820
13
            break;
5821
10
        case 87:
5822
10
            if (!session->context->passive &&
5823
8
                15 > session->driver.ubx.protver) {
5824
3
                unsigned i;
5825
5826
                /* protver 14 or less
5827
                 * turn off 15 and above UBX-NAV.  Do we need to? */
5828
3
                msg[0] = 0x01;          // class, UBX-NAV
5829
3
                msg[2] = 0x00;          // rate, off
5830
18
                for (i = 0; i < ROWS(ubx_15_nav_on); i++) {
5831
15
                    msg[1] = ubx_15_nav_on[i];          // msg id to turn off
5832
15
                    (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5833
15
                }
5834
3
            }
5835
10
            break;
5836
10
        case 90:
5837
            // Turn off some clutter, no need to do it early
5838
10
            if (!session->context->passive &&
5839
8
                15 <= session->driver.ubx.protver &&
5840
5
                27 > session->driver.ubx.protver) {
5841
                /* protver 15 or more, and less than 27
5842
                 * Soturn off 14 and below UBX-NAV */
5843
2
                unsigned i;
5844
5845
2
                msg[0] = 0x01;          // class, UBX-NAV
5846
2
                msg[2] = 0x00;          // rate, off
5847
6
                for (i = 0; i < ROWS(ubx_14_nav_on); i++) {
5848
4
                    msg[1] = ubx_14_nav_on[i];          // msg id to turn off
5849
4
                    (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
5850
4
                }
5851
2
            }
5852
10
            break;
5853
5
        case 93:
5854
            /* finish up by checking if we overflowed the input buffer
5855
             * request MON-RXBUF/TXBUFF, or MON-COMMS */
5856
5
            if (27 > session->driver.ubx.protver) {
5857
                // MON-RXBUF and MON-TXBUF
5858
2
                (void)ubx_write(session, UBX_CLASS_MON, 0x08, NULL, 0);
5859
2
                (void)ubx_write(session, UBX_CLASS_MON, 0x07, NULL, 0);
5860
3
            } else {
5861
                // MON-COMMS
5862
3
                (void)ubx_write(session, UBX_CLASS_MON, 0x36, NULL, 0);
5863
3
            }
5864
5
            break;
5865
5.06k
        default:
5866
5.06k
            break;
5867
6.46k
        }
5868
6.46k
        session->queue++;
5869
6.46k
    }
5870
19.1k
    return mask | ONLINE_SET;
5871
19.1k
}
5872
5873
static gps_mask_t parse_input(struct gps_device_t *session)
5874
20.8k
{
5875
20.8k
    if (UBX_PACKET == session->lexer.type) {
5876
19.1k
        return ubx_parse(session, session->lexer.outbuffer,
5877
19.1k
                         session->lexer.outbuflen);
5878
19.1k
    }
5879
1.68k
    return generic_parse_input(session);
5880
20.8k
}
5881
5882
// not used by gpsd, it's for gpsctl and friends
5883
static ssize_t ubx_control_send(struct gps_device_t *session, char *msg,
5884
                                size_t data_len)
5885
0
{
5886
0
    return ubx_write(session, (unsigned int)msg[0], (unsigned int)msg[1],
5887
0
                     (unsigned char *)msg + 2,
5888
0
                     (size_t)(data_len - 2)) ? ((ssize_t) (data_len + 7)) : -1;
5889
0
}
5890
5891
static void ubx_init_query(struct gps_device_t *session)
5892
406
{
5893
    // UBX-MON-VER: query for version information
5894
406
    (void)ubx_write(session, UBX_CLASS_MON, 0x04, NULL, 0);
5895
5896
    /* We can't get query for UBX-SEC-UNIQID as we need the protver first.
5897
     * Plus, we want to chain requests so as not to  overflow the receiver
5898
     * inbuffers. */
5899
406
}
5900
5901
static void ubx_event_hook(struct gps_device_t *session, event_t event)
5902
28.9k
{
5903
28.9k
    if (session->context->readonly) {
5904
9.60k
        return;
5905
9.60k
    }
5906
19.3k
    if (event == EVENT_IDENTIFIED) {
5907
332
        GPSD_LOG(LOG_PROG, &session->context->errout, "UBX identified\n");
5908
5909
        // no longer set UBX-CFG-SBAS here, u-blox 9 and 10 do not have it
5910
5911
332
        if (session->context->passive) {
5912
            /* passive mode, do no autoconfig
5913
             * but we really want MON-VER. */
5914
78
            (void)ubx_write(session, UBX_CLASS_MON, 0x04, NULL, 0);
5915
254
        } else if (O_OPTIMIZE == session->mode) {
5916
            // Turn off UBX output, turn on NMEA on this port.
5917
0
            ubx_mode(session, MODE_BINARY);
5918
254
        } else {
5919
            //* Turn off NMEA output, turn on UBX on this port.
5920
254
            ubx_mode(session, MODE_NMEA);
5921
254
        }
5922
18.9k
    } else if (event == EVENT_DEACTIVATE) {
5923
        /* There used to be a hotstart/reset here.
5924
         * That caused u-blox USB to re-enumerate.
5925
         * Sometimes to a new device name.
5926
         * Bad.  Don't do that anymore...
5927
         */
5928
0
    }
5929
19.3k
}
5930
5931
// generate and send a configuration block
5932
static gps_mask_t ubx_cfg_prt(struct gps_device_t *session, speed_t speed,
5933
                              const char parity,
5934
                              const int stopbits, const int mode)
5935
254
{
5936
254
    unsigned long usart_mode = 0;
5937
254
    unsigned char buf[UBX_CFG_LEN];
5938
254
    unsigned long i;
5939
5940
254
    memset(buf, '\0', UBX_CFG_LEN);
5941
5942
    /*
5943
     * When this is called from gpsd, the initial probe for UBX should
5944
     * have picked up the device's port number from the CFG_PRT response.
5945
     */
5946
    // FIXME!  Bad test, port_id == 0 is valid too.  DDC (I2X) = port 0
5947
254
    if (session->driver.ubx.port_id != 0) {
5948
122
        buf[0] = session->driver.ubx.port_id;
5949
122
    }
5950
    /*
5951
     * This default can be hit if we haven't sent a CFG_PRT query yet,
5952
     * which can happen in gpsmon because it doesn't autoprobe.
5953
     *
5954
     * What we'd like to do here is dispatch to USART1_ID or
5955
     * USB_ID intelligently based on whether this is a USB or RS232
5956
     * source.  Unfortunately the GR601-W screws that up by being
5957
     * a USB device with port_id 1.  So we bite the bullet and
5958
     * default to port 1.
5959
     *
5960
     * Without further logic, this means gpsmon wouldn't be able to
5961
     * change the speed on the EVK 6H's USB port.  But! To pick off
5962
     * the EVK 6H on Linux as a special case, we notice that its
5963
     * USB device name is /dev/ttyACMx - it presents as a USB modem.
5964
     *
5965
     * This logic will fail on any USB u-blox device that presents
5966
     * as an ordinary USB serial device (/dev/ttyUSB*) and actually
5967
     * has port ID 3 the way it "ought" to.
5968
     */
5969
132
    else if (strstr(session->gpsdata.dev.path, "/ttyACM") != NULL) {
5970
        // using the built in USB port
5971
        // FIXME!!  USB port has no speed!
5972
        // FIXME!!  maybe we know the portid already?
5973
0
        session->driver.ubx.port_id = buf[0] = USB_ID;
5974
132
    } else {
5975
        // A guess.  Could be UART2, or SPI, or DDC port
5976
132
        session->driver.ubx.port_id = buf[0] = USART1_ID;
5977
132
    }
5978
5979
254
    putle32(buf, 8, speed);
5980
5981
    /*
5982
     * u-blox tech support explains the default contents of the mode
5983
     * field as follows:
5984
     *
5985
     * D0 08 00 00     mode (LSB first)
5986
     *
5987
     * re-ordering bytes: 000008D0
5988
     * dividing into fields: 000000000000000000 00 100 0 11 0 1 0000
5989
     * nStopbits = 00 = 1
5990
     * parity = 100 = none
5991
     * charLen = 11 = 8-bit
5992
     * reserved1 = 1
5993
     *
5994
     * The protocol reference further gives the following subfield values:
5995
     * 01 = 1.5 stop bits (?)
5996
     * 10 = 2 stopbits
5997
     * 000 = even parity
5998
     * 001 = odd parity
5999
     * 10x = no parity
6000
     * 10 = 7 bits
6001
     *
6002
     * Some UBX reference code amplifies this with:
6003
     *
6004
     *   prtcfg.mode = (1<<4) | // compatibility with ANTARIS 4
6005
     *                 (1<<7) | // charLen = 11 = 8 bit
6006
     *                 (1<<6) | // charLen = 11 = 8 bit
6007
     *                 (1<<11); // parity = 10x = none
6008
     */
6009
254
    usart_mode |= (1<<4);       // reserved1 Antaris 4 compatibility bit
6010
254
    usart_mode |= (1<<7);       // high bit of charLen
6011
6012
    // u-blox 5+ binary only supports 8N1
6013
254
    switch (parity) {
6014
0
    case (int)'E':
6015
0
    case 2:
6016
0
        usart_mode |= (1<<7);           // 7E
6017
0
        break;
6018
0
    case (int)'O':
6019
0
    case 1:
6020
0
        usart_mode |= (1<<9) | (1<<7);  // 7O
6021
0
        break;
6022
254
    case (int)'N':
6023
254
    case 0:
6024
254
    default:
6025
254
        usart_mode |= (1<<11) | (3<<6); // 8N
6026
254
        break;
6027
254
    }
6028
6029
254
    if (2 == stopbits) {
6030
0
        usart_mode |= (1<<13);
6031
0
    }
6032
6033
254
    putle32(buf, 4, usart_mode);
6034
6035
    // enable all input protocols by default
6036
    // RTCM3 is protver 20+
6037
254
    buf[12] = NMEA_PROTOCOL_MASK | UBX_PROTOCOL_MASK | RTCM_PROTOCOL_MASK |
6038
254
              RTCM3_PROTOCOL_MASK;
6039
6040
    /* enable all input protocols by default
6041
     * no u-blox has RTCM2 out
6042
     * RTCM3 is protver 20+ */
6043
254
    buf[outProtoMask] = NMEA_PROTOCOL_MASK | UBX_PROTOCOL_MASK |
6044
254
                        RTCM3_PROTOCOL_MASK;
6045
    // FIXME: use VALGET if protver  24+
6046
254
    (void)ubx_write(session, UBX_CLASS_CFG, 0x00, buf, sizeof(buf));
6047
6048
254
    GPSD_LOG(LOG_PROG, &session->context->errout,
6049
254
             "UBX ubx_cfg_prt mode %d port %d PROTVER %d\n", mode, buf[0],
6050
254
             session->driver.ubx.protver);
6051
6052
    // selectively enable output protocols
6053
254
    if (mode == MODE_NMEA) {
6054
        /*
6055
         * We have to club the GR601-W over the head to make it stop emitting
6056
         * UBX after we've told it to start.  But do not mung the
6057
         * protocol out mask, that breaks things.
6058
         */
6059
6060
254
        unsigned char msg[3];
6061
        /* nmea to turn on at rate one (multiplier on measurement rate)
6062
         * u-blox 8 default: RMC, VTG, GGA, GSA GSV, GLL
6063
         * who wanted GST? */
6064
254
        const unsigned char nmea_on[] = {
6065
254
            0x00,          // msg id  = GGA
6066
            // 0x01,          // msg id  = GLL, only need RMC
6067
254
            0x02,          // msg id  = GSA
6068
254
            0x03,          // msg id  = GSV
6069
254
            0x04,          // msg id  = RMC
6070
254
            0x05,          // msg id  = VTG
6071
254
            0x07,          // msg id  = GST, GNSS pseudorange error statistics
6072
254
            0x08,          // msg id  = ZDA, for UTC year
6073
254
            0x09,          // msg id  = GBS, for RAIM errors
6074
254
        };
6075
6076
254
        const unsigned char ubx_nav_off[] = {
6077
254
            0x01,          // msg id = NAV-POSECEF
6078
254
            0x04,          // msg id = UBX-NAV-DOP
6079
254
            0x06,          // msg id = NAV-SOL, deprecated in 6, gone in 9
6080
254
            0x07,          // msg id = NAV-PVT, in u-blox 6 and on
6081
254
            0x11,          // msg id = NAV-VELECEF
6082
254
            0x20,          // msg id = UBX-NAV-TIMEGPS
6083
            // 0x26;       // msg id  = UBX-NAV-TIMELS, allow as low rate
6084
254
            0x30,          // msg id = NAV-SVINFO, in 4 to 8, not 9
6085
254
            0x32,          // msg id = NAV-SBAS, in u-blox 4 to 8, not all 9
6086
254
            0x35,          // msg id = NAV-SAT, in u-blox 8 and up
6087
254
            0x43,          // msg id = NAV-SIG, in u-blox 9 and up
6088
254
            0x61,          // msg id = NAV-EOE
6089
254
        };
6090
6091
        // turn off init queue
6092
254
        session->queue = 0;
6093
6094
        // enable NMEA first, in case we over-run receiver input buffer.
6095
6096
        // turn on rate one NMEA
6097
254
        msg[0] = 0xf0;          // class, NMEA
6098
254
        msg[2] = 0x01;          // rate, one
6099
2.28k
        for (i = 0; i < sizeof(nmea_on); i++) {
6100
2.03k
            msg[1] = nmea_on[i];          // msg id to turn on
6101
2.03k
            (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
6102
2.03k
        }
6103
6104
        // Now turn off UBX-NAV, one at a time.
6105
254
        msg[0] = 0x01;          // class, UBX-NAV
6106
254
        msg[2] = 0x00;          // rate off
6107
3.04k
        for (i = 0; i < sizeof(ubx_nav_off); i++) {
6108
2.79k
            msg[1] = ubx_nav_off[i];          // msg id to turn on
6109
2.79k
            (void)ubx_write(session, UBX_CLASS_CFG, 0x01, msg, 3);
6110
2.79k
        }
6111
6112
254
    } else {    // MODE_BINARY
6113
6114
        /*
6115
         * Just enabling the UBX protocol for output is not enough to
6116
         * actually get UBX output; the sentence mix is initially empty.
6117
         * Fix that...
6118
         */
6119
6120
        /* Beware sending too many messages without waiting
6121
         * for u-blox ACK, over running its input buffer.
6122
         *
6123
         * For example, the UBX-MON-VER may fail here, but works in other
6124
         * contexts.
6125
         *
6126
         * Need UBX-MON-VER for protver.  Need protver to properly configure
6127
         * the message set.
6128
         */
6129
0
        unsigned char msg[3] = {0, 0, 0};
6130
        // request SW and HW Versions, prolly already requested at detection
6131
        // ask again as older u-blox are hard of hearing
6132
0
        (void)ubx_write(session, UBX_CLASS_MON, 0x04, msg, 0);
6133
6134
0
        GPSD_LOG(LOG_IO, &session->context->errout, "UBX: init protVer %u\n",
6135
0
                 session->driver.ubx.protver);
6136
6137
        // turn on init queue
6138
0
        session->queue = 1;
6139
0
    }
6140
254
    return 0;
6141
254
}
6142
6143
static void ubx_mode(struct gps_device_t *session, int mode)
6144
254
{
6145
254
    ubx_cfg_prt(session,
6146
254
                gpsd_get_speed(session),
6147
254
                gpsd_get_parity(session),
6148
254
                gpsd_get_stopbits(session),
6149
254
                mode);
6150
254
}
6151
6152
static bool ubx_speed(struct gps_device_t *session,
6153
                      speed_t speed, char parity, int stopbits)
6154
0
{
6155
0
    ubx_cfg_prt(session,
6156
0
                speed,
6157
0
                parity,
6158
0
                stopbits,
6159
0
                (session->lexer.type == UBX_PACKET) ? MODE_BINARY : MODE_NMEA);
6160
0
    return true;
6161
0
}
6162
6163
/* change the sample rate of the GPS */
6164
static bool ubx_rate(struct gps_device_t *session, double cycletime)
6165
0
{
6166
    /* Minimum measurement cycle time currently known from documentation
6167
     * for fastest devices, here in milli seconds. Maintained in
6168
     * struct gps_type_t driver_ubx.
6169
     */
6170
0
    const int64_t min_cycle = TSTOMS(&session->device_type->min_cycle);
6171
    // cycletime in milli seconds
6172
0
    int64_t measRate = (int64_t)(cycletime * MS_IN_SEC);
6173
    // Message to be sent to device
6174
0
    unsigned char msg[6] = {
6175
0
        0x00, 0x00,     // U2: Measurement rate (ms), will be set below
6176
0
        0x01, 0x00,     // U2: Navigation rate (cycles), set to 1
6177
0
        0x00, 0x00,     // U2: Alignment to reference time: 0 = UTC
6178
0
    };
6179
6180
    // check max
6181
0
    if (65535 < measRate) {
6182
0
        measRate = 65535;   // milli seconds
6183
0
    } else if (min_cycle > measRate) {
6184
        /* Clamp cycle time to lowest bound given in documentation.
6185
         * protVer >= 24 has 25 ms min.
6186
         * protVer < 24 has min of 50ms or more.
6187
         */
6188
0
        measRate = min_cycle;
6189
0
    }
6190
    // we now know measRate fits in a U2
6191
6192
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
6193
0
             "UBX rate change, measRate %lld millisecs\n",
6194
0
             (long long) measRate);
6195
0
    msg[0] = (unsigned char)(measRate & 0xff);
6196
0
    msg[1] = (unsigned char)(measRate >> 8);
6197
6198
    // UBX-CFG-RATE deprecated in u-blox 10
6199
0
    return ubx_write(session, UBX_CLASS_CFG, 0x08, msg, 6); // CFG-RATE
6200
0
}
6201
6202
// This is everything we export
6203
// *INDENT-OFF*
6204
const struct gps_type_t driver_ubx = {
6205
    .type_name         = "u-blox",       // Full name of type
6206
    .packet_type       = UBX_PACKET,     // associated lexer packet type
6207
    .flags             = DRIVER_STICKY,  // remember this
6208
    .trigger           = NULL,
6209
    // Number of satellite channels supported by the device
6210
    // ZED-F0T supports 60, ZED-F0P supports 184
6211
    .channels          = 184,
6212
    .probe_detect      = NULL,           // Startup-time device detector
6213
    // Packet getter (using default routine)
6214
    .get_packet        = packet_get1,
6215
    .parse_packet      = parse_input,    // Parse message packets
6216
    // RTCM handler (using default routine)
6217
    .rtcm_writer       = gpsd_write,
6218
    .init_query        = ubx_init_query, // non-perturbing initial query
6219
    .event_hook        = ubx_event_hook, // Fire on various lifetime events
6220
    .speed_switcher    = ubx_speed,      // Speed (baudrate) switch
6221
    .mode_switcher     = ubx_mode,       // Mode switcher
6222
    .rate_switcher     = ubx_rate,       // Message delivery rate switcher
6223
    /* Minimum measurement cycle time currently known from documentation
6224
     * for fastest devices.
6225
     */
6226
    .min_cycle.tv_sec  = 0,
6227
    .min_cycle.tv_nsec = 25000000,          // Maximum 40Hz sample rate
6228
    .control_send      = ubx_control_send,  // how to send a control string
6229
    .time_offset       = NULL,              // no method for NTP fudge factor
6230
};
6231
// *INDENT-ON*
6232
6233
// vim: set expandtab shiftwidth=4