Coverage Report

Created: 2026-06-04 06:21

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