Coverage Report

Created: 2025-03-14 06:43

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