Coverage Report

Created: 2026-08-31 06:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_allystar.c
Line
Count
Source
1
/*
2
 * skeletal ALLYSTAR driver
3
 *
4
 * ALLYSTAR binary is a sloppy copy of UBX Binary.
5
 * The message structure is the same:
6
 *  header, ID, length, payload, checksum
7
 *
8
 * Header changed feom the UBX 0xb5 0x62 to 0xf1 0xd9
9
 * IDs are similar
10
 * The length, and content, of some payloads are change.
11
 * Checksum algorithm is the same.
12
 *
13
 * This file is Copyright Gary E  Miller
14
 * This file is Copyright by the GPSD project
15
 * SPDX-License-Identifier: BSD-2-clause
16
 */
17
18
#include "../include/gpsd_config.h"  // must be before all includes
19
20
#include <math.h>
21
#include <stdbool.h>
22
#include <stdio.h>
23
#include <stdlib.h>                // for abs()
24
#include <string.h>
25
26
#include "../include/gpsd.h"
27
#include "../include/bits.h"
28
29
// one byte class
30
typedef enum {
31
    ALLY_NAV = 0x01,     // Navigation
32
    ALLY_RXM = 0x02,     // Receiver Manager Messages
33
    ALLY_ACK = 0x05,     // (Not) Acknowledges for cfg messages
34
    ALLY_CFG = 0x06,     // Configuration requests
35
    ALLY_MON = 0x0a,     // System monitoring
36
    ALLY_AID = 0x0b,     // AGPS (Deprecated)
37
    ALLY_NMEA = 0xf0,    // NMEA, for CFG-MSG
38
    ALLY_RTCM = 0xf8,    // RTCM, for CFG-MSG
39
} ally_classes_t;
40
41
#define MSGID(cls_, id_) (((cls_)<<8)|(id_))
42
43
typedef enum {
44
    ACK_ACK         = MSGID(ALLY_ACK, 0x01),
45
    ACK_NAK         = MSGID(ALLY_ACK, 0x00),
46
    CFG_PRT         = MSGID(ALLY_CFG, 0x00),
47
    CFG_MSG         = MSGID(ALLY_CFG, 0x01),
48
    CFG_PPS         = MSGID(ALLY_CFG, 0x07),
49
    CFG_CFG         = MSGID(ALLY_CFG, 0x09),
50
    CFG_DOP         = MSGID(ALLY_CFG, 0x0a),
51
    CFG_ELEV        = MSGID(ALLY_CFG, 0x0b),
52
    CFG_NAVSAT      = MSGID(ALLY_CFG, 0x0c),
53
    CFG_HEIGHT      = MSGID(ALLY_CFG, 0x0d),
54
    CFG_SBAS        = MSGID(ALLY_CFG, 0x0e),
55
    CFG_SPDHOLD     = MSGID(ALLY_CFG, 0x0f),
56
    CFG_EPHSAVE     = MSGID(ALLY_CFG, 0x10),
57
    CFG_NUMSV       = MSGID(ALLY_CFG, 0x11),
58
    CFG_SURVEY      = MSGID(ALLY_CFG, 0x12),  // HD9200/HD9400 only
59
    CFG_FIXEDLLA    = MSGID(ALLY_CFG, 0x13),  // HD9200/HD9400 only
60
    CFG_FIXEDECEF   = MSGID(ALLY_CFG, 0x14),  // HD9200/HD9400 only
61
    CFG_ANTIJAM     = MSGID(ALLY_CFG, 0x15),
62
    CFG_BDGEO       = MSGID(ALLY_CFG, 0x16),
63
    CFG_CARRSMOOTH  = MSGID(ALLY_CFG, 0x17),
64
    CFG_GEOFENCE    = MSGID(ALLY_CFG, 0x18),
65
    CFG_SIMPLERST   = MSGID(ALLY_CFG, 0x40),
66
    CFG_SLEEP       = MSGID(ALLY_CFG, 0x41),
67
    CFG_PWRCTL      = MSGID(ALLY_CFG, 0x42),
68
    CFG_NMEAVER     = MSGID(ALLY_CFG, 0x43),
69
    CFG_PWRCTL2     = MSGID(ALLY_CFG, 0x44),
70
    MON_VER         = MSGID(ALLY_MON, 0x04),
71
    MON_INFO        = MSGID(ALLY_MON, 0x05),
72
    MON_TRKCHAN     = MSGID(ALLY_MON, 0x08),
73
    MON_RCVCLK      = MSGID(ALLY_MON, 0x09),
74
    MON_CWI         = MSGID(ALLY_MON, 0x0a),
75
    NAV_POSECEF     = MSGID(ALLY_NAV, 0x01),
76
    NAV_POSLLH      = MSGID(ALLY_NAV, 0x02),
77
    NAV_DOP         = MSGID(ALLY_NAV, 0x04),
78
    NAV_TIME        = MSGID(ALLY_NAV, 0x05),
79
    NAV_VELECEF     = MSGID(ALLY_NAV, 0x11),
80
    NAV_VELNED      = MSGID(ALLY_NAV, 0x12),
81
    NAV_TIMEUTC     = MSGID(ALLY_NAV, 0x21),
82
    NAV_CLOCK       = MSGID(ALLY_NAV, 0x22),
83
    NAV_CLOCK2      = MSGID(ALLY_NAV, 0x23),
84
    NAV_PVERR       = MSGID(ALLY_NAV, 0x26),
85
    NAV_SVINFO      = MSGID(ALLY_NAV, 0x30),
86
    NAV_SVSTATE     = MSGID(ALLY_NAV, 0x32),
87
    NAV_AUTO        = MSGID(ALLY_NAV, 0xc0),
88
    NAV_PVT         = MSGID(ALLY_NAV, 0xc1),
89
    /* NMEA_* for CFG-MSG, source:
90
     * https://docs.datagnss.com/rtk-board/#9download-the-latest-firmware
91
     */
92
    NMEA_GGA        = MSGID(ALLY_NMEA, 0x00),
93
    NMEA_GLL        = MSGID(ALLY_NMEA, 0x01),
94
    NMEA_GSA        = MSGID(ALLY_NMEA, 0x02),
95
    NMEA_GRS        = MSGID(ALLY_NMEA, 0x03),
96
    NMEA_GSV        = MSGID(ALLY_NMEA, 0x04),
97
    NMEA_RMC        = MSGID(ALLY_NMEA, 0x05),
98
    NMEA_VTG        = MSGID(ALLY_NMEA, 0x06),
99
    NMEA_ZDA        = MSGID(ALLY_NMEA, 0x07),
100
    NMEA_TXT        = MSGID(ALLY_NMEA, 0x20),
101
    /* RTCM_* for CFG-MSG, source:
102
     * https://docs.datagnss.com/rtk-board/#9download-the-latest-firmware
103
     */
104
    RTCM_1005       = MSGID(ALLY_RTCM, 0x05),
105
    RTCM_1019       = MSGID(ALLY_RTCM, 0x12),
106
    RTCM_1020       = MSGID(ALLY_RTCM, 0x14),
107
    RTCM_1077       = MSGID(ALLY_RTCM, 0x4d),
108
    RTCM_1087       = MSGID(ALLY_RTCM, 0x57),
109
    RTCM_1097       = MSGID(ALLY_RTCM, 0x06),
110
    RTCM_1117       = MSGID(ALLY_RTCM, 0x75),
111
    RTCM_1127       = MSGID(ALLY_RTCM, 0x7f),
112
    // RXM-*
113
    RXM_DUMPRAW     = MSGID(ALLY_RXM, 0x01),   // -DUM and -DUMPRAW the same??
114
    RXM_DUM         = MSGID(ALLY_RXM, 0x01),
115
    RXM_GALSAR      = MSGID(ALLY_RXM, 0x02),
116
    RXM_UNK         = MSGID(ALLY_RXM, 0x57),
117
} ally_msgs_t;
118
119
// 2 bytes leader, 2 bytes ID, 2 bytes payload length
120
7.95k
#define ALLY_PREFIX_LEN 6
121
122
static struct vlist_t vclass[] = {
123
    {0x01, "NAV"},
124
    {0x02, "RXM"},
125
    {0x05, "ACK"},
126
    {0x06, "CFG"},
127
    {0x0a, "MON"},
128
    {0x0b, "AID"},
129
    {0xf0, "NMEA"},
130
    {0xf8, "RTCM"},
131
    {0, NULL},
132
};
133
134
//CFG-ANTIJAM statsys_mask
135
static struct flist_t fsatsys[] = {
136
    {0x02, 0x02, "GPS"},
137
    {0x04, 0x04, "QZSS"},
138
    {0x08, 0x08, "SBAS"},
139
    {0x10, 0x10, "GAL"},
140
    {0x20, 0x20, "BDS"},
141
    {0x40, 0x40, "GLO"},
142
    {0, 0, NULL},
143
};
144
145
// CFG-GEOFENCE  cfg_flag
146
static struct vlist_t vcfg_flags[] = {
147
    {0, "None"},
148
    {1, "68%"},
149
    {2, "95%"},
150
    {3, "99.7%"},
151
    {4, "99.99%"},
152
    {5, "99.99999%"},
153
    {0, NULL},
154
};
155
156
/* CFG-NAVSAT enableMask
157
 * NAV-CLOCK2 sysmask */
158
static struct flist_t fenableMask[] = {
159
    {1, 1, "GPS L1"},
160
    {2, 2, "GLO G1"},
161
    {4, 4, "BDS B1"},
162
    {8, 8, "QZSS L1S"},
163
    {0x10, 0x10, "GAL E1"},
164
    {0x20, 0x20, "QZSS L1"},
165
    {0x40, 0x40, "SBAS L1"},
166
    {0x80, 0x80, "NAVIC L5"},
167
    {0x100, 0x100, "GPS L1C"},
168
    {0x200, 0x800, "GPS L5"},
169
    {0x400, 0x400, "GPS L2C"},
170
    {0x800, 0x40000, "x800"},
171
    {0x1000, 0x40000, "x1000"},
172
    {0x2000, 0x2000, "GLO G2"},
173
    {0x4000, 0x4000, "BDS B1C"},
174
    {0x8000, 0x8000, "BDS B2A"},
175
    {0x10000, 0x10000, "BDS B3I"},
176
    {0x20000, 0x20000, "BDS B5"},
177
    {0x40000, 0x40000, "BDS B2"},
178
    {0x80000, 0x80000, "0x80000"},
179
    {0x100000, 0x100000, "GAL E5A"},
180
    {0x200000, 0x200000, "GAL E5B"},
181
    {0x400000, 0x400000, "GAL E6"},
182
    {0x800000, 0x800000, "0x800000"},
183
    {0x1000000, 0x1000000, "QZSS L6"},
184
    {0x2000000, 0x2000000, "QZSS L1C"},
185
    {0x4000000, 0x4000000, "QZSS L5"},
186
    {0x8000000, 0x8000000, "QZSS L2C"},
187
    {0, 0, NULL},
188
};
189
190
// CFG-NMEAVER NMEA versions
191
static struct vlist_t vversions[] = {
192
    {0, "None"},
193
    {1, "V3.01"},
194
    {2, "V4.00"},
195
    {3, "V4.10"},
196
    {0, NULL},
197
};
198
199
// CFG-PRT, portID
200
static struct vlist_t vportID[] = {
201
    {0, "UART0"},
202
    {1, "UART1"},
203
    {0, NULL},
204
};
205
206
// NAV-AUTO fixstate
207
static struct vlist_t vfixstate[] = {
208
    {0, "None"},
209
    {1, "Aided"},         // ??
210
    {2, "Clock Bias"},    // ??
211
    {3, "2D"},
212
    {4, "3D"},
213
    {5, "DGNSS"},
214
    {6, "RTTK Flt"},
215
    {7, "RTTK Fix"},
216
    {0, NULL},
217
};
218
219
// NAV-PVT fixType fixstate
220
static struct vlist_t vfixType[] = {
221
    {0, "None"},
222
    {1, "DR"},
223
    {2, "2D"},
224
    {3, "3D"},
225
    {4, "Surveyed"},
226
    {0, NULL},
227
};
228
229
/* NAV-SVINFO flags
230
 * Undocumented.
231
 * This is a guess based on UBX-NAV-SVINFO
232
 * Only  diffCorr,orbitAvail,orbitEphs seem to be used. */
233
static struct flist_t fsvinfo_flags[] = {
234
    {1, 1, "svUsed"},
235
    {2, 2, "diffCorr"},
236
    {4, 4, "orbitAvail"},
237
    {8, 8, "orbitEph"},
238
    {0x10, 0x10, "unhealthy"},
239
    {0x20, 0x20, "orbitAlm"},
240
    {0x40, 0x40, "orbitAop"},
241
    {0x80, 0x80, "smoothed"},
242
    {0, 0, NULL},
243
};
244
245
// NAV-TIME flags
246
static struct flist_t vtime_flags[] = {
247
    {1, 1, "week"},
248
    {2, 2, "second"},
249
    {4, 4, "leapsecond"},
250
    {0, 0, NULL},
251
};
252
253
// NAV-TIME navSys
254
// What does 15 or 21 mean??
255
static struct vlist_t vtime_navsys[] = {
256
    {0, "GPS"},
257
    {1, "BDS"},
258
    {2, "GLO"},
259
    {3, "GAL"},
260
    {0, NULL},
261
};
262
263
// NAV-TIMEUTC utcStandard
264
static struct vlist_t vtimeutc_utc[] = {
265
    {0x00, "NA"},
266
    {0x10, "NTSC"},
267
    {0x20, "USNO"},
268
    {0x30, "3"},
269
    {0x40, "EUL"},
270
    {0x50, "SU"},
271
    {0x60, "INDIA"},
272
    {0x0, NULL},
273
};
274
275
// NAV-TIMEUTC ValidFlag
276
static struct flist_t vtimeutc_valid[] = {
277
    {1, 1, "Valid tow"},
278
    {2, 2, "Valid week"},
279
    {4, 4, "Valid UTC"},    // Valid Leapsecond
280
    {0, 0, NULL},
281
};
282
283
// NAV-SVSTATE health
284
static struct vlist_t vsvstate_hlth[] = {
285
    {0, "Unk"},
286
    {1, "Healthy"},
287
    {2, "Unhealthy"},
288
    {0, NULL},
289
};
290
291
// NAV-SVSTATE eph_cource, alm_source
292
static struct vlist_t vsvstate_src[] = {
293
    {0, "NA"},
294
    {1, "GNSS"},
295
    {2, "Aided"},
296
    {0, NULL},
297
};
298
299
// NAV-SVSTATE visibility
300
static struct vlist_t vsvstate_viz[] = {
301
    {0, "Unk"},
302
    {1, "Below Hrz"},
303
    {2, "Above Hrz"},
304
    {3, "Above mask"},
305
    {0, NULL},
306
};
307
308
/* send a ALLYSTAR message.
309
 * calculate checksums, etc.
310
 */
311
bool ally_write(struct gps_device_t * session,
312
                unsigned int msg_class, unsigned int msg_id,
313
                const unsigned char *msg, size_t payload_len)
314
6.66k
{
315
6.66k
    unsigned char CK_A, CK_B;
316
6.66k
    ssize_t count;
317
6.66k
    size_t i;
318
6.66k
    bool ok;
319
320
    // do not write if -b (readonly) option set
321
    // "passive" handled earlier
322
6.66k
    if (session->context->readonly) {
323
0
        return true;
324
0
    }
325
326
6.66k
    session->msgbuf[0] = 0xf1;
327
6.66k
    session->msgbuf[1] = 0xd9;
328
329
6.66k
    CK_A = CK_B = 0;
330
6.66k
    session->msgbuf[2] = msg_class;
331
6.66k
    session->msgbuf[3] = msg_id;
332
6.66k
    session->msgbuf[4] = payload_len & 0xff;
333
6.66k
    session->msgbuf[5] = (payload_len >> 8) & 0xff;
334
335
6.66k
    if ((sizeof(session->msgbuf) - 8) <= payload_len) {
336
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
337
0
                 "=> GPS: ALLYL class: %02x, id: %02x, len: %zd TOO LONG!\n",
338
0
                 msg_class, msg_id, payload_len);
339
0
    }
340
6.66k
    if (NULL != msg &&
341
3.87k
        0 < payload_len) {
342
3.87k
        (void)memcpy(&session->msgbuf[ALLY_PREFIX_LEN], msg, payload_len);
343
3.87k
    }
344
345
    // calculate CRC
346
44.6k
    for (i = 2; i < (6 + payload_len); i++) {
347
37.9k
        CK_A += session->msgbuf[i];
348
37.9k
        CK_B += CK_A;
349
37.9k
    }
350
351
6.66k
    session->msgbuf[6 + payload_len] = CK_A;
352
6.66k
    session->msgbuf[7 + payload_len] = CK_B;
353
6.66k
    session->msgbuflen = payload_len + 8;
354
355
6.66k
    GPSD_LOG(LOG_PROG, &session->context->errout,
356
6.66k
             "=> GPS: ALLY: class: %02x, id: %02x, len: %zd, crc: %02x%02x\n",
357
6.66k
             msg_class, msg_id, payload_len,
358
6.66k
             CK_A, CK_B);
359
6.66k
    count = gpsd_write(session, session->msgbuf, session->msgbuflen);
360
6.66k
    ok = (count == (ssize_t) session->msgbuflen);
361
6.66k
    return ok;
362
6.66k
}
363
364
/* ALLYSTAR "svid" to ubx_gnssid, ubx_svid, ubs_sigid
365
 * Sort of documented in the $GPGSV section.
366
 * But the ranges overlap, so many PRNs are ambiguous...
367
 *
368
 * Returns ubx_svid
369
           0 on error
370
 */
371
static unsigned ally_svid_to_ids(struct gps_device_t *session,
372
                                 unsigned svid, unsigned *ubx_gnssid,
373
                                 unsigned *ubx_sigid)
374
30.4k
{
375
30.4k
    unsigned ubx_svid = 0;
376
30.4k
    *ubx_gnssid = 0;
377
30.4k
    *ubx_sigid = 0;
378
379
30.4k
    if (IN(1, svid, 32)) {
380
        // GPS
381
694
        *ubx_gnssid = 0;
382
694
        *ubx_sigid = 0;
383
694
        ubx_svid = svid;
384
29.7k
    } else if (IN(40, svid, 54)) {
385
        // SBAS 40-54, not 40-58??
386
66
        *ubx_gnssid = 1;
387
66
        *ubx_sigid = 0;
388
66
        ubx_svid = svid + 80;
389
29.7k
    } else if (IN(65, svid, 96)) {
390
        // GLONASS G1 OF
391
581
        *ubx_gnssid = 6;
392
581
        *ubx_sigid = 0;
393
581
        ubx_svid = svid;
394
29.1k
    } else if (IN(127, svid, 141)) {
395
        // SBAS, 127-151, not 120-158?
396
189
        *ubx_gnssid = 1;
397
189
        *ubx_sigid = 0;
398
189
        ubx_svid = svid;
399
28.9k
    } else if (IN(193, svid, 199)) {
400
        // QZSS L1 C/A
401
131
        *ubx_gnssid = 5;
402
131
        *ubx_sigid = 0;
403
131
        ubx_svid = svid - 192;
404
28.8k
    } else if (IN(201, svid, 263)) {
405
        // BDS B1
406
442
        *ubx_gnssid = 3;
407
442
        *ubx_sigid = 0;
408
442
        ubx_svid = svid - 200;
409
28.3k
    } else if (IN(301, svid, 336)) {
410
        // Galileo E1 C
411
127
        *ubx_gnssid = 2;
412
127
        *ubx_sigid = 0;
413
127
        ubx_svid = svid - 300;
414
28.2k
    } else if (IN(401, svid, 432)) {
415
        // GPS L1C NMEA sig 9
416
124
        *ubx_gnssid = 0;
417
124
        *ubx_sigid = 0;    // wrong...
418
124
        ubx_svid = svid - 400;
419
28.1k
    } else if (IN(501, svid, 532)) {
420
        // GPS L2CM
421
189
        *ubx_gnssid = 2;
422
189
        *ubx_sigid = 4;
423
189
        ubx_svid = svid - 500;
424
27.9k
    } else if (IN(565, svid, 596)) {
425
        // GLONASS G2
426
333
        *ubx_gnssid = 6;
427
333
        *ubx_sigid = 2;
428
333
        ubx_svid = svid - 564;
429
27.6k
    } else if (IN(601, svid, 663)) {
430
        // BDS B1C, overlaps GPS L6, 651-682!
431
68
        *ubx_gnssid = 3;
432
68
        *ubx_sigid = 5;      // 5 or 6??
433
68
        ubx_svid = svid - 600;
434
27.5k
    } else if (IN(651, svid, 682)) {
435
        // GPS L5, overlaps BDS B1C 601-663
436
66
        *ubx_gnssid = 0;
437
66
        *ubx_sigid = 0;      // wrong....
438
66
        ubx_svid = svid - 650;
439
27.4k
    } else if (IN(701, svid, 763)) {
440
        // BDS B1I
441
88
        *ubx_gnssid = 3;
442
88
        *ubx_sigid = 1;      // could be 0 or 1?
443
88
        ubx_svid = svid - 700;
444
27.3k
    } else if (IN(801, svid, 863)) {
445
        // BDS B3I, overlaps BDS B2A, 851-914
446
70
        *ubx_gnssid = 3;
447
70
        *ubx_sigid = 2;      // wrong....
448
70
        ubx_svid = svid - 800;
449
27.3k
    } else if (IN(843, svid, 849)) {
450
        /* QZSS, L5, overlaps BDS B3I, 801-863
451
         * We know it is not possible, but it is what the doc says.
452
         * Coverity 493913
453
         * coverity[DEADCODE}  */
454
0
        *ubx_gnssid = 5;
455
0
        *ubx_sigid = 9;      // ??
456
0
        ubx_svid = svid - 842;
457
27.3k
    } else if (IN(851, svid, 913)) {
458
        // BDS B2A - overlaps BDS B3I 801-863, IRNSS L5 901-918
459
67
        *ubx_gnssid = 3;
460
67
        *ubx_sigid = 7;      // ??
461
67
        ubx_svid = svid - 850;
462
27.2k
    } else if (IN(901, svid, 918)) {
463
        // NavIC (IRNSS) L5
464
0
        *ubx_gnssid = 7;
465
0
        *ubx_sigid = 7;      // ??
466
0
        ubx_svid = svid - 900;
467
27.2k
    } else if (IN(951, svid, 986)) {
468
        // GAL E5A
469
68
        *ubx_gnssid = 2;
470
68
        *ubx_sigid = 3;      // Could be 3 or 4?
471
68
        ubx_svid = svid - 950;
472
27.1k
    } else {
473
27.1k
        GPSD_LOG(LOG_WARN, &session->context->errout,
474
27.1k
                 "ALLY:ally_svid_to_ids(%u) unknown svid\n", svid);
475
27.1k
        *ubx_gnssid = 0;
476
27.1k
        ubx_svid = 0;
477
27.1k
    }
478
30.4k
    if (200 < ubx_svid) {
479
        // Huh?  Paranoia.
480
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
481
0
                 "ALLY:ally_svid_to_ids(%u) gnssid %u usvid %u usigid %u\n",
482
0
                 svid, *ubx_gnssid, ubx_svid,  *ubx_sigid);
483
0
        *ubx_sigid = 0;
484
0
        *ubx_gnssid = 0;
485
0
        ubx_svid = 0;
486
0
     }
487
30.4k
    return ubx_svid;
488
30.4k
}
489
490
// ACK-*
491
492
// ACK-ACK
493
static gps_mask_t msg_ack_ack(struct gps_device_t *session,
494
                              unsigned char *buf, size_t payload_len UNUSED)
495
100
{
496
100
    GPSD_LOG(LOG_PROG, &session->context->errout,
497
100
             "ALLY: ACK-ACK: class: %02x(%s), id: %02x\n",
498
100
              buf[0], val2str(buf[0], vclass), buf[1]);
499
100
    return 0;
500
100
}
501
502
// ACK-NAK
503
static gps_mask_t msg_ack_nak(struct gps_device_t *session,
504
                              unsigned char *buf, size_t payload_len UNUSED)
505
134
{
506
134
    GPSD_LOG(LOG_PROG, &session->context->errout,
507
134
             "ALLY: ACK-NAK: class: %02x(%s), id: %02x\n",
508
134
              buf[0], val2str(buf[0], vclass), buf[1]);
509
134
    return 0;
510
134
}
511
512
// CFG-*
513
514
/**
515
 * CFG-ANTIJAM - Constellations in useantijam settings
516
 *
517
 */
518
static gps_mask_t msg_cfg_antijam(struct gps_device_t *session,
519
                                  unsigned char *buf,
520
                                  size_t payload_len UNUSED)
521
0
{
522
0
    char buf2[80];
523
0
    unsigned satsys_mask = getleu16(buf, 0);
524
0
    unsigned threshold = getub(buf, 2);
525
526
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
527
0
             "ALLY: CFG-ANTIJAM: satsys_mask x%x(%s) threshold %u\n",
528
0
             satsys_mask,
529
0
             flags2str(satsys_mask, fsatsys, buf2, sizeof(buf2)),
530
0
             threshold);
531
0
    return 0;
532
0
}
533
534
/**
535
 * CFG_CARRSMOOTH
536
 *
537
 */
538
static gps_mask_t msg_cfg_carrsmooth(struct gps_device_t *session,
539
                                     unsigned char *buf,
540
                                     size_t payload_len UNUSED)
541
0
{
542
0
    int windows_value = getsb(buf, 0);
543
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
544
0
             "ALLY: CFG-CARRSMOOTH: windows_value %d\n",
545
0
             windows_value);
546
0
    return 0;
547
0
}
548
549
// CFG_FIXEDLLA    alt is altHAE, per datagnss.
550
551
/**
552
 * CFG-GEOFENCE -- genfences
553
 *
554
 */
555
static gps_mask_t msg_cfg_geofence(struct gps_device_t *session,
556
                                   unsigned char *buf, size_t payload_len)
557
0
{
558
0
    unsigned idx;
559
0
    unsigned llr_num = getub(buf, 0);
560
0
    unsigned cfg_flag = getub(buf, 1);
561
0
    unsigned gpio_enable = getub(buf, 2);
562
0
    unsigned polarity = getub(buf, 13);
563
0
    unsigned gpionum = getub(buf, 14);
564
565
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
566
0
             "ALLY: CFG-GEOFENCE: llr_num %u cfg_flag %u(%s) gpio_enable %u "
567
0
             "polarity %u gpionum %u\n",
568
0
             llr_num, cfg_flag,
569
0
             val2str(cfg_flag, vcfg_flags),
570
0
             gpio_enable, polarity, gpionum);
571
572
0
    if ((8 + (12 * llr_num)) != payload_len) {
573
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
574
0
                 "ALLY: CFG-GEOFENCE odd payload len %zu\n",
575
0
                 payload_len);
576
0
    }
577
578
0
    for (idx = 8; idx < payload_len; idx += 12) {
579
0
        double lon = getles32(buf, idx) / 1e7;
580
0
        double lat = getles32(buf, idx + 4) / 1e7;
581
0
        double radius = getleu16(buf, idx + 8) / 100.0;
582
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
583
0
                 "ALLY: CFG-GEOFENCE: lat %.4f lon %.4f rad %.2f\n",
584
0
                 lat, lon, radius);
585
0
    }
586
0
    return 0;
587
0
}
588
589
/**
590
 * CFG-NAVSAT - Constellations in use
591
 *
592
 */
593
static gps_mask_t msg_cfg_navsat(struct gps_device_t *session,
594
                                 unsigned char *buf, size_t payload_len UNUSED)
595
0
{
596
0
    char buf2[200];
597
0
    unsigned long long enableMask = getleu32(buf, 0);
598
599
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
600
0
             "ALLY: CFG-NAVSAT: enableMask x%llx(%s)\n",
601
0
             enableMask,
602
0
             flags2str(enableMask, fenableMask, buf2, sizeof(buf2)));
603
0
    return 0;
604
0
}
605
606
/**
607
 * CFG-NMEAVER - NMEA version
608
 *
609
 */
610
static gps_mask_t msg_cfg_nmeaver(struct gps_device_t *session,
611
                                  unsigned char *buf,
612
                                  size_t payload_len UNUSED)
613
0
{
614
0
    unsigned version = getub(buf, 0);
615
616
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
617
0
             "ALLY: CFG-NMEAVER: version %u(%s)\n",
618
0
             version,
619
0
             val2str(version, vversions));
620
0
    return 0;
621
0
}
622
623
/**
624
 * CFG-PPS -- PPS settings
625
 *
626
 * payload 5 on early chips (Cynosure I)
627
 *         12 on later chops
628
 *
629
 */
630
static gps_mask_t msg_cfg_pps(struct gps_device_t *session,
631
                                  unsigned char *buf, size_t payload_len)
632
0
{
633
0
    if (5 == payload_len) {
634
0
        unsigned long long length = getleu32(buf, 0);
635
0
        unsigned polarity  = getub(buf, 4);
636
637
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
638
0
                 "ALLY: CFG-PPS: length %llu polarity %u\n",
639
0
                 length, polarity);
640
0
    } else if (15 == payload_len) {
641
0
        unsigned long long period = getleu32(buf, 0);
642
0
        long long offset = getleu32(buf, 4);
643
0
        unsigned long long dutyCycle = getleu32(buf, 8);
644
0
        unsigned polarity  = getub(buf, 12);
645
0
        unsigned gpio  = getub(buf, 13);
646
0
        unsigned sync  = getub(buf, 14);
647
648
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
649
0
                 "ALLY: CFG-PPS: period %llu offset %lld duty %llu "
650
0
                 "polarity %u gpio %u sync %u\n",
651
0
                 period, offset, dutyCycle,  polarity, gpio, sync);
652
0
    } else {
653
        // doc mentions payload 12, but no details.
654
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
655
0
                 "ALLY: CFG-PPS: odd payload len %zd\n",
656
0
                 payload_len);
657
0
    }
658
659
0
    return 0;
660
0
}
661
662
/**
663
 * CFG-PRT -- Coomm port settings
664
 *
665
 * Sadly, no way to tell what port is the current port!
666
 *
667
 */
668
static gps_mask_t msg_cfg_prt(struct gps_device_t *session,
669
                              unsigned char *buf, size_t payload_len UNUSED)
670
66
{
671
66
    unsigned portID  = getub(buf, 0);
672
66
    unsigned long long baudrate = getleu32(buf, 4);
673
674
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
675
66
             "ALLY: CFG-PRT: portID %u(%s) baudrate %llu\n",
676
66
             portID, val2str(portID, vportID), baudrate);
677
678
66
    return 0;
679
66
}
680
681
/**
682
 * CFG-SBAS - SBAS in use
683
 *
684
 */
685
static gps_mask_t msg_cfg_sbas(struct gps_device_t *session,
686
                               unsigned char *buf, size_t payload_len)
687
0
{
688
0
    unsigned idx;
689
690
0
    if (0 == payload_len) {
691
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
692
0
                 "ALLY: CFG-SBAS: no SBAS enabled\n");
693
0
        return 0;
694
0
    }
695
696
0
    for (idx = 0; idx < payload_len; idx += 2) {
697
0
        unsigned PRN = getub(buf, idx);
698
0
        unsigned enabled = getub(buf, idx + 1);
699
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
700
0
                 "ALLY: CFG-SBAS: PRN %u enabled: %u\n",
701
0
                 PRN, enabled);
702
0
    }
703
0
    return 0;
704
0
}
705
706
// MON-*
707
708
/**
709
 * Receiver/Software Version
710
 * MON-VER
711
 * kinda sorta like early UBX-MON-VER if you squint real hard.
712
 *
713
 * buf points to payload.
714
 * payload_len is length of payload.
715
 *
716
 */
717
static gps_mask_t msg_mon_ver(struct gps_device_t *session,
718
                              unsigned char *buf,
719
                              size_t payload_len UNUSED)
720
18
{
721
    // save SW and HW Version as subtype
722
18
    (void)snprintf(session->subtype, sizeof(session->subtype),
723
18
                   "SW %.16s,HW %.16s",
724
18
                   (char *)buf,
725
18
                   (char *)(buf + 16));
726
727
18
    GPSD_LOG(LOG_PROG, &session->context->errout,
728
18
             "ALLY: MON-VER: %s\n",
729
18
             session->subtype);
730
731
18
    return 0;
732
18
}
733
734
// NAV-*
735
736
/**
737
 * NAV-AUTO -- GNSS info
738
 *
739
 */
740
static gps_mask_t msg_nav_auto(struct gps_device_t *session,
741
                                unsigned char *buf, size_t payload_len UNUSED)
742
201
{
743
201
    gps_mask_t mask = 0;
744
201
    unsigned mode = MODE_NOT_SEEN;
745
201
    unsigned status = STATUS_UNK;
746
201
    struct tm unpacked_date = {0};
747
748
201
    unsigned fixstate = getub(buf, 0);
749
201
    unsigned year = getleu16(buf, 1);
750
201
    unsigned month = getub(buf, 3);
751
201
    unsigned day = getub(buf, 4);
752
201
    unsigned hour = getub(buf, 5);
753
201
    unsigned min = getub(buf, 6);
754
201
    unsigned sec = getub(buf, 7);
755
201
    long long lon = getles32(buf, 8);
756
201
    long long lat = getles32(buf, 12);
757
201
    long long altHAE = getles32(buf, 16);
758
    // no place for speed_3d....
759
201
    unsigned speed_3d = getleu16(buf, 20);
760
201
    int heading = getles16(buf, 22);
761
201
    unsigned pDOP = getles16(buf, 24);
762
201
    unsigned hDOP = getles16(buf, 26);
763
201
    unsigned vDOP = getles16(buf, 28);
764
    /* We don't use satInUse, satInView.  ALLYSTAR counts two signals from
765
     * one sat, as one sat */
766
201
    unsigned satInUse = getub(buf, 30);
767
201
    unsigned satInView = getub(buf, 31);
768
769
201
    switch (fixstate) {
770
131
    case 0:    // No fix
771
131
        mode |= MODE_NO_FIX;
772
131
        status = STATUS_UNK;
773
131
        mask |= STATUS_SET | MODE_SET;
774
131
        break;
775
4
    case 1:    // Aided fix ??
776
4
        FALLTHROUGH
777
4
    case 2:    // Clock bias fix ??
778
        // assume these are like surveyed-in
779
4
        mode = MODE_3D;
780
4
        status = STATUS_TIME;
781
4
        mask |= STATUS_SET | MODE_SET;
782
4
        break;
783
0
    case 3:         // 2D fix
784
0
        mode = MODE_2D;
785
0
        status = STATUS_GPS;
786
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
787
0
        break;
788
0
    case 4:         // 3D fix
789
0
        mode = MODE_3D;
790
0
        status = STATUS_GPS;
791
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
792
0
        break;
793
0
    case 5:         // DGNSS
794
0
        mode = MODE_3D;
795
0
        status = STATUS_DGPS;
796
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
797
0
        break;
798
0
    case 6:         // RTK Float
799
0
        mode = MODE_3D;
800
0
        status = STATUS_RTK_FLT;
801
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
802
0
        break;
803
0
    case 7:         // RTK Fix
804
0
        mode = MODE_3D;
805
0
        status = STATUS_RTK_FIX;
806
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
807
0
        break;
808
66
    default:
809
        // huh?
810
66
        break;
811
201
    }
812
201
    session->newdata.mode = mode;
813
201
    session->newdata.status = status;
814
815
    // We don't know if time, or leapseconds, is valid.
816
201
    unpacked_date.tm_year = year - 1900;
817
201
    unpacked_date.tm_mon = month - 1;
818
201
    unpacked_date.tm_mday = day;
819
201
    unpacked_date.tm_hour = hour;
820
201
    unpacked_date.tm_min = min;
821
201
    unpacked_date.tm_sec = sec;
822
201
    session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
823
    // If rate higher than 1Hz, we have no idea of sub-seconds...
824
201
    session->newdata.time.tv_nsec = 0;
825
201
    TS_NORM(&session->newdata.time);
826
201
    if (3 <= fixstate) {
827
        // not sure about states 1 and 2
828
66
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
829
66
    }
830
831
201
    session->newdata.longitude = lon / 1e7;
832
201
    session->newdata.latitude = lat / 1e7;
833
201
    session->newdata.altHAE = altHAE / 1e3;
834
835
201
    session->newdata.track = heading / 100.0;
836
201
    mask |= TRACK_SET;
837
838
201
    if (9999 > pDOP) {
839
69
        session->gpsdata.dop.pdop = pDOP / 100.0;
840
69
        mask |= DOP_SET;
841
69
    }
842
201
    if (9999 > hDOP) {
843
133
        session->gpsdata.dop.hdop = hDOP / 100.0;
844
133
        mask |= DOP_SET;
845
133
    }
846
201
    if (9999 > vDOP) {
847
69
        session->gpsdata.dop.vdop = vDOP / 100.0;
848
69
        mask |= DOP_SET;
849
69
    }
850
201
    GPSD_LOG(LOG_PROG, &session->context->errout,
851
201
             "ALLY: NAV-AUTO: time %lld fixstate %u %u/%u/%u "
852
201
             "%u:%02u:%02u lon %.7f lat %.7f altHAE %.3f "
853
201
             "speed_3d %u, heading %.2f pDOP %.2f hDOP %.2f vDOP %.2f "
854
201
             "satInUse %u satInView %u\n",
855
201
             (long long)session->newdata.time.tv_sec,
856
201
             fixstate,
857
201
             year, month, day, hour, min, sec,
858
201
             session->newdata.longitude,
859
201
             session->newdata.latitude,
860
201
             session->newdata.altHAE,
861
201
             speed_3d,
862
201
             session->newdata.track,
863
201
             session->gpsdata.dop.pdop,
864
201
             session->gpsdata.dop.hdop,
865
201
             session->gpsdata.dop.vdop,
866
201
             satInUse, satInView);
867
201
     GPSD_LOG(LOG_IO, &session->context->errout,
868
201
              "ALLY: NAV-SUTO: fixstate %u(%s) mode %u(%s) status %u(%s)\n",
869
201
             fixstate, val2str(fixstate, vfixstate),
870
201
             session->newdata.mode,
871
201
             val2str(session->newdata.mode, vmode_str),
872
201
             session->newdata.status,
873
201
             val2str(session->newdata.status, vstatus_str));
874
201
    return mask;
875
201
}
876
877
/**
878
 * Clock Solution NAV-CLOCK
879
 * Seems to match UBX-NAV-CLOCK
880
 *
881
 */
882
static gps_mask_t msg_nav_clock(struct gps_device_t *session,
883
                                unsigned char *buf, size_t payload_len UNUSED)
884
66
{
885
66
    unsigned long tAcc = getleu32(buf, 12);
886
66
    unsigned long fAcc = getleu32(buf, 16);
887
888
66
    session->driver.ubx.iTOW = getleu32(buf, 0);
889
66
    session->gpsdata.fix.clockbias = getles32(buf, 4);
890
66
    session->gpsdata.fix.clockdrift = getles32(buf, 8);
891
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
892
66
             "ALLY: NAV-CLOCK: iTOW=%lld clkB %ld clkD %ld tAcc %lu fAcc %lu\n",
893
66
             (long long)session->driver.ubx.iTOW,
894
66
             session->gpsdata.fix.clockbias,
895
66
             session->gpsdata.fix.clockdrift,
896
66
             tAcc, fAcc);
897
66
    return 0;
898
66
}
899
900
/**
901
 * NAV-CLOCK2 Sat Clock Solution
902
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
903
 *
904
 */
905
static gps_mask_t msg_nav_clock2(struct gps_device_t *session,
906
                                 unsigned char *buf, size_t payload_len)
907
192
{
908
192
    char buf2[80];
909
192
    unsigned u;
910
911
192
    unsigned long long numClk = getleu32(buf, 4);
912
192
    session->driver.ubx.iTOW = getleu32(buf, 0);
913
914
636
    for (u = 8; u < payload_len; u += 12) {
915
444
        unsigned long long sysmask = getleu32(buf, u);
916
444
        long long clkB = getles32(buf, u + 4);              // ns
917
444
        unsigned long long tAcc = getleu16(buf, u + 8);     // ns
918
919
444
        GPSD_LOG(LOG_PROG, &session->context->errout,
920
444
                 "ALLY: CFG-CLOCK2: sysmask x%llx(%s) clkB %lld yAcc %llu\n",
921
444
                 sysmask,
922
444
                 flags2str(sysmask, fenableMask, buf2, sizeof(buf2)),
923
444
                 clkB, tAcc);
924
444
    }
925
926
192
    GPSD_LOG(LOG_PROG, &session->context->errout,
927
192
             "ALLY: NAV-CLOCK2: iTOW=%lld numClk %lld\n",
928
192
             (long long)session->driver.ubx.iTOW,
929
192
             numClk);
930
192
    return 0;
931
192
}
932
933
/**
934
 * NAV-DOP, Dilution of precision message
935
 * Seems to match UBX-NAV-DOP
936
 *
937
 */
938
static gps_mask_t msg_nav_dop(struct gps_device_t *session,
939
                              unsigned char *buf, size_t payload_len UNUSED)
940
374
{
941
374
    unsigned u;
942
374
    gps_mask_t mask = 0;
943
944
374
    session->driver.ubx.iTOW = getleu32(buf, 0);  // in milli seconds
945
    /*
946
     * We make a deliberate choice not to clear DOPs from the
947
     * last skyview here, but rather to treat this as a supplement
948
     * to our calculations from the visibility matrix, trusting
949
     * the firmware algorithms over ours.
950
     */
951
374
    u = getleu16(buf, 4);
952
374
    if (9999 > u) {
953
156
        session->gpsdata.dop.gdop = (double)(u / 100.0);
954
156
        mask |= DOP_SET;
955
156
    }
956
374
    u = getleu16(buf, 6);
957
374
    if (9999 > u) {
958
285
        session->gpsdata.dop.pdop = (double)(u / 100.0);
959
285
        mask |= DOP_SET;
960
285
    }
961
374
    u = getleu16(buf, 8);
962
374
    if (9999 > u) {
963
304
        session->gpsdata.dop.tdop = (double)(u / 100.0);
964
304
        mask |= DOP_SET;
965
304
    }
966
374
    u = getleu16(buf, 10);
967
374
    if (9999 > u) {
968
205
        session->gpsdata.dop.vdop = (double)(u / 100.0);
969
205
        mask |= DOP_SET;
970
205
    }
971
374
    u = getleu16(buf, 12);
972
374
    if (9999 > u) {
973
196
        session->gpsdata.dop.hdop = (double)(u / 100.0);
974
196
        mask |= DOP_SET;
975
196
    }
976
    // Northing DOP
977
374
    u = getleu16(buf, 14);
978
374
    if (9999 > u) {
979
132
        session->gpsdata.dop.ydop = (double)(u / 100.0);
980
132
        mask |= DOP_SET;
981
132
    }
982
    // Easting DOP
983
374
    u = getleu16(buf, 16);
984
374
    if (9999 > u) {
985
169
        session->gpsdata.dop.xdop = (double)(u / 100.0);
986
169
        mask |= DOP_SET;
987
169
    }
988
374
    GPSD_LOG(LOG_PROG, &session->context->errout,
989
374
             "ALLY: NAV-DOP: gdop=%.2f pdop=%.2f "
990
374
             "hdop=%.2f vdop=%.2f tdop=%.2f ydop=%.2f xdop=%.2f\n",
991
374
             session->gpsdata.dop.gdop,
992
374
             session->gpsdata.dop.hdop,
993
374
             session->gpsdata.dop.vdop,
994
374
             session->gpsdata.dop.pdop, session->gpsdata.dop.tdop,
995
374
             session->gpsdata.dop.ydop, session->gpsdata.dop.xdop);
996
374
    return mask;
997
374
}
998
999
/*
1000
 * Navigation Position ECEF message -- NAV-POSECEF
1001
 * Looks same as UBX-NAV-POSECEF
1002
 *
1003
 * This message does not bother to tell us if it is valid.
1004
 */
1005
static gps_mask_t msg_nav_posecef(struct gps_device_t *session,
1006
                                 unsigned char *buf, size_t payload_len UNUSED)
1007
52
{
1008
52
    gps_mask_t mask = ECEF_SET;
1009
1010
52
    session->driver.ubx.iTOW = getleu32(buf, 0);
1011
    // all in cm
1012
52
    session->newdata.ecef.x = getles32(buf, 4) / 100.0;
1013
52
    session->newdata.ecef.y = getles32(buf, 8) / 100.0;
1014
52
    session->newdata.ecef.z = getles32(buf, 12) / 100.0;
1015
52
    session->newdata.ecef.pAcc = getleu32(buf, 16) / 100.0;
1016
1017
    // (long long) cast for 32-bit compat
1018
52
    GPSD_LOG(LOG_PROG, &session->context->errout,
1019
52
        "ALLY: NAV-POSECEF: iTOW=%lld ECEF x=%.2f y=%.2f z=%.2f pAcc=%.2f\n",
1020
52
        (long long)session->driver.ubx.iTOW,
1021
52
        session->newdata.ecef.x,
1022
52
        session->newdata.ecef.y,
1023
52
        session->newdata.ecef.z,
1024
52
        session->newdata.ecef.pAcc);
1025
52
    return mask;
1026
52
}
1027
1028
/*
1029
 * Geodetic position solution message
1030
 * NAV-POSLLH, Class 1, ID 2
1031
 * Seems same as UBX-MON-POSLLH
1032
 *
1033
 * This message does not bother to tell us if it is valid.
1034
 * No mode, so limited usefulness
1035
 */
1036
static gps_mask_t msg_nav_posllh(struct gps_device_t *session,
1037
                                 unsigned char *buf,
1038
                                 size_t payload_len UNUSED)
1039
73
{
1040
73
    gps_mask_t mask = 0;
1041
1042
73
    session->driver.ubx.iTOW = getleu32(buf, 0);
1043
73
    session->newdata.longitude = getles32(buf, 4) / 1e7;
1044
73
    session->newdata.latitude = getles32(buf, 8) / 1e7;
1045
    // altitude WGS84
1046
73
    session->newdata.altHAE = getles32(buf, 12) / 1e3;
1047
    // altitude MSL
1048
73
    session->newdata.altMSL = getles32(buf, 16) / 1e3;
1049
    // Let gpsd_error_model() deal with geoid_sep
1050
1051
    // Horizontal accuracy estimate in mm, unknown type
1052
73
    session->newdata.eph = getleu32(buf, 20) / 1e3;
1053
    // Vertical accuracy estimate in mm, unknown type
1054
73
    session->newdata.epv = getleu32(buf, 24) / 1e3;
1055
1056
73
    GPSD_LOG(LOG_PROG, &session->context->errout,
1057
73
        "ALLY: NAV-POSLLH: iTOW=%lld lat=%.3f lon=%.3f altHAE=%.3f "
1058
73
        "eph %.3f epv %.3f\n",
1059
73
        (long long)session->driver.ubx.iTOW,
1060
73
        session->newdata.latitude,
1061
73
        session->newdata.longitude,
1062
73
        session->newdata.altHAE,
1063
73
        session->newdata.eph,
1064
73
        session->newdata.epv);
1065
1066
73
    mask = ONLINE_SET | HERR_SET | VERR_SET | LATLON_SET | ALTITUDE_SET;
1067
73
    return mask;
1068
73
}
1069
1070
/*
1071
 * Positioning velocity error estimation
1072
 * NAV-PVERR, Class 1, ID x26
1073
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1074
 * UBX has no equivalent
1075
 * Most of the data in $GPGST, but missing the ellipsoid data.
1076
 */
1077
static gps_mask_t msg_nav_pverr(struct gps_device_t *session,
1078
                                unsigned char *buf,
1079
                                size_t payload_len UNUSED)
1080
69
{
1081
69
    gps_mask_t mask = 0;
1082
1083
    // Probably NOT fix time
1084
69
    session->driver.ubx.iTOW = getleu32(buf, 0);
1085
    // latitude, millimeters
1086
69
    session->gpsdata.gst.lat_err_deviation = getles32(buf, 4)/ 1000.0;
1087
    // longitude, millimeters
1088
69
    session->gpsdata.gst.lon_err_deviation = getles32(buf, 4)/ 1000.0;
1089
    // altitude, millimeters
1090
69
    session->gpsdata.gst.alt_err_deviation = getles32(buf, 4)/ 1000.0;
1091
    // velocity east, millimeters / second
1092
69
    session->gpsdata.gst.ve_err_deviation  = getles32(buf, 4)/ 1000.0;
1093
    // velocity north, millimeters / second
1094
69
    session->gpsdata.gst.vn_err_deviation = getles32(buf, 4)/ 1000.0;
1095
    // velocity up, millimeters / second
1096
69
    session->gpsdata.gst.vu_err_deviation = getles32(buf, 4)/ 1000.0;
1097
1098
69
    GPSD_LOG(LOG_PROG, &session->context->errout,
1099
69
        "ALLY: NAV-PVERR: iTOW %lld stdlat %.3f stdlon %.3f stdalt %.3f "
1100
69
        "stdve %.3f stdvn %.3f stdvu %.3f\n",
1101
69
        (long long)session->driver.ubx.iTOW,
1102
69
        session->gpsdata.gst.lat_err_deviation,
1103
69
        session->gpsdata.gst.lon_err_deviation,
1104
69
        session->gpsdata.gst.alt_err_deviation,
1105
69
        session->gpsdata.gst.ve_err_deviation,
1106
69
        session->gpsdata.gst.vn_err_deviation,
1107
69
        session->gpsdata.gst.vu_err_deviation);
1108
1109
69
    mask = GST_SET | ONLINE_SET;
1110
69
    return mask;
1111
69
}
1112
1113
/**
1114
 * NAV-PVT
1115
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1116
 *
1117
 */
1118
static gps_mask_t msg_nav_pvt(struct gps_device_t *session,
1119
                              unsigned char *buf, size_t payload_len UNUSED)
1120
343
{
1121
343
    gps_mask_t mask = 0;
1122
343
    unsigned mode = MODE_NOT_SEEN;
1123
343
    unsigned status = STATUS_UNK;
1124
343
    struct tm unpacked_date = {0};
1125
343
    timespec_t ts_tow;
1126
1127
343
    unsigned year = getleu16(buf, 4);
1128
343
    unsigned month = getub(buf, 6);
1129
343
    unsigned day = getub(buf, 7);
1130
343
    unsigned hour = getub(buf, 8);
1131
343
    unsigned min = getub(buf, 9);
1132
343
    unsigned sec = getub(buf, 10);
1133
343
    unsigned valid = getub(buf, 10);      // undocumented!
1134
343
    unsigned long long tAcc = getleu32(buf, 12);
1135
343
    long long nano = getles32(buf, 16);
1136
343
    unsigned fixType = getub(buf, 20);
1137
    // 21, 22 reserved
1138
    /* We don't use numSV.  ALLYSTAR counts two signals from
1139
     * one sat, as one sat */
1140
343
    unsigned numSV = getub(buf, 23);
1141
343
    long long lon = getles32(buf, 24);
1142
343
    long long lat = getles32(buf, 28);
1143
343
    long long altHAE = getles32(buf, 32);
1144
343
    long long hMSL = getles32(buf, 36);
1145
343
    unsigned long long hAcc = getleu32(buf, 40);
1146
343
    unsigned long long vAcc = getleu32(buf, 44);
1147
343
    long long Vel_N = getles32(buf, 48);
1148
343
    long long Vel_E = getles32(buf, 52);
1149
343
    long long Vel_D = getles32(buf, 56);
1150
343
    long long gSpeed = getles32(buf, 60);        // signed??
1151
343
    unsigned long long headMot = getles32(buf, 64);
1152
343
    long long sAcc = getleu32(buf, 68);
1153
343
    long long headAcc = getleu32(buf, 72);
1154
343
    int pDOP = getles16(buf, 76);
1155
    // Different than headMot ??
1156
343
    unsigned long long headVeh = getles32(buf, 84);
1157
1158
343
    session->driver.ubx.iTOW = getleu32(buf, 0);
1159
343
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1160
1161
343
    switch (fixType) {
1162
135
    case 0:    // No fix
1163
135
        mode |= MODE_NO_FIX;
1164
135
        status = STATUS_UNK;
1165
135
        mask |= STATUS_SET | MODE_SET;
1166
135
        break;
1167
2
    case 1:    // Aided fix ??
1168
2
        FALLTHROUGH
1169
2
    case 2:    // Clock bias fix ??
1170
        // assume these are like surveyed-in
1171
2
        mode = MODE_3D;
1172
2
        status = STATUS_TIME;
1173
2
        mask |= STATUS_SET | MODE_SET;
1174
2
        break;
1175
18
    case 3:         // 2D fix
1176
18
        mode = MODE_2D;
1177
18
        status = STATUS_GPS;
1178
18
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1179
18
        break;
1180
0
    case 4:         // 3D fix
1181
0
        mode = MODE_3D;
1182
0
        status = STATUS_GPS;
1183
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1184
0
        break;
1185
0
    case 5:         // DGNSS
1186
0
        mode = MODE_3D;
1187
0
        status = STATUS_DGPS;
1188
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1189
0
        break;
1190
0
    case 6:         // RTK Float
1191
0
        mode = MODE_3D;
1192
0
        status = STATUS_RTK_FLT;
1193
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1194
0
        break;
1195
0
    case 7:         // RTK Fix
1196
0
        mode = MODE_3D;
1197
0
        status = STATUS_RTK_FIX;
1198
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1199
0
        break;
1200
188
    default:
1201
        // huh?
1202
188
        break;
1203
343
    }
1204
343
    session->newdata.mode = mode;
1205
343
    session->newdata.status = status;
1206
1207
343
    unpacked_date.tm_year = year;
1208
343
    unpacked_date.tm_mon = month;
1209
343
    unpacked_date.tm_mday = day;
1210
343
    unpacked_date.tm_hour = hour;
1211
343
    unpacked_date.tm_min = min;
1212
343
    unpacked_date.tm_sec = sec;
1213
    // FIXME:  add in fractional iTOW
1214
343
    session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
1215
343
    session->newdata.time.tv_nsec = nano;
1216
343
    TS_NORM(&session->newdata.time);
1217
343
    if (0 < fixType) {
1218
        // accept DR
1219
208
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
1220
208
    }
1221
1222
343
    session->newdata.longitude = lon / 1e7;
1223
343
    session->newdata.latitude = lat / 1e7;
1224
343
    session->newdata.altHAE = altHAE / 1e3;
1225
343
    session->newdata.altMSL = hMSL / 1e3;
1226
343
    session->newdata.speed = gSpeed / 1e3;
1227
1228
343
    session->newdata.track = headMot / 100.0;
1229
343
    mask |= TRACK_SET;
1230
1231
343
    if (9999 > pDOP) {
1232
132
        session->gpsdata.dop.pdop = pDOP / 100.0;
1233
132
        mask |= DOP_SET;
1234
132
    }
1235
343
    session->newdata.NED.velN = Vel_N / 1000.0;
1236
343
    session->newdata.NED.velE = Vel_E / 1000.0;
1237
343
    session->newdata.NED.velD = Vel_D / 1000.0;
1238
1239
343
    GPSD_LOG(LOG_PROG, &session->context->errout,
1240
343
             "ALLY: NAV-PVT: time %lld%.09ld %u/%u/%u %u:%02u:%02u valid x%x "
1241
343
             "tAcc %llu fixType %u  numSv %u lon %.7f lat %.7f "
1242
343
             "altHAE %.3f altMSL %.3f hAcc %llu vAcc %llu "
1243
343
             "speed %.3f, headMot %.2f sAcc ^%.55f headAcc %.5f pDOP %.2f "
1244
343
             "headVeh %.2f\n",
1245
343
             (long long)session->newdata.time.tv_sec,
1246
343
             session->newdata.time.tv_nsec,
1247
343
             year, month, day, hour, min, sec,
1248
343
             valid, tAcc,
1249
343
             fixType, numSV,
1250
343
             session->newdata.longitude,
1251
343
             session->newdata.latitude,
1252
343
             session->newdata.altHAE,
1253
343
             session->newdata.altMSL,
1254
343
             hAcc, vAcc,
1255
343
             session->newdata.speed,
1256
343
             session->newdata.track,
1257
343
             sAcc / 1e5, headAcc / 1e5,
1258
343
             session->gpsdata.dop.pdop,
1259
343
             headVeh / 1e5);
1260
343
     GPSD_LOG(LOG_IO, &session->context->errout,
1261
343
              "ALLY: NAV-PVT fixType %u(%s) mode %u(%s) status %u(%s)\n",
1262
343
             fixType, val2str(fixType, vfixType),
1263
343
             session->newdata.mode,
1264
343
             val2str(session->newdata.mode, vmode_str),
1265
343
             session->newdata.status,
1266
343
             val2str(session->newdata.status, vstatus_str));
1267
343
    return mask;
1268
343
}
1269
1270
/**
1271
 * GPS Satellite Info -- NAV-SVINFO
1272
 * Sort of like UBX-NAV-SVINFO
1273
 *   UBX is (8 + 12 * numCh)
1274
 *   ALLYSTAR if (8 + 24 *numCh)
1275
 *
1276
 * NOT USED BECAUSE:
1277
 *   some fields not documented: flags, quality
1278
 *   reports a cno, but does not say for L1, L2, etc.
1279
 *   no way to know if a sat used, or unhealthy
1280
 *
1281
 * Have to use NMEA for this data.
1282
 *
1283
 */
1284
static gps_mask_t msg_nav_svinfo(struct gps_device_t *session,
1285
                               unsigned char *buf, size_t payload_len)
1286
547
{
1287
    // char buf2[80];
1288
547
    unsigned i, nsv, st;
1289
547
    uint32_t nchan;
1290
547
    timespec_t ts_tow;
1291
1292
547
    session->driver.ubx.iTOW = getleu32(buf, 0);
1293
547
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1294
547
    session->gpsdata.skyview_time =
1295
547
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
1296
1297
547
    nchan = getleu32(buf, 4);  // 32 bits, seriously??
1298
547
    if (nchan > MAXCHANNELS) {
1299
66
        GPSD_LOG(LOG_WARN, &session->context->errout,
1300
66
                 "ALLY: NAV SVINFO: runt >%d reported visible\n",
1301
66
                 MAXCHANNELS);
1302
66
        return 0;
1303
66
    }
1304
1305
481
    if ((nchan * 24 + 8) > payload_len) {
1306
66
        GPSD_LOG(LOG_WARN, &session->context->errout,
1307
66
                 "ALLY: NAV SVINFO: runt payload %d channels\n",
1308
66
                 nchan);
1309
66
        return 0;
1310
66
    }
1311
1312
415
    gpsd_zero_satellites(&session->gpsdata);
1313
415
    nsv = 0;
1314
1.45k
    for (i = st = 0; i < nchan; i++) {
1315
1.03k
        char buf2[80];
1316
        // No info on sigid???
1317
1.03k
        unsigned off = 24 * i;
1318
1.03k
        unsigned nmea_PRN;
1319
        // v 2.3.1, 8 bit chand and 8-bit svid became 16-bit svid.
1320
        // no doc on svid numbers... same as NMEA??
1321
1.03k
        unsigned ally_svid = getleu16(buf, off + 8);
1322
        // no doc on flags
1323
1.03k
        unsigned flags = getub(buf, off + 10);
1324
        // no doc on quality
1325
1.03k
        unsigned quality = getub(buf, off + 11);
1326
1.03k
        unsigned cno = getub(buf, off + 12);
1327
1.03k
        int el = getsb(buf, off + 13);
1328
1.03k
        int az = getles16(buf, off + 14);
1329
1.03k
        long long prRes = getles32(buf, off + 16);  // pseudorange residue, cm
1330
1.03k
        unsigned ubx_gnssid, ubx_sigid;
1331
1332
1.03k
        unsigned ubx_svid = ally_svid_to_ids(session, ally_svid, &ubx_gnssid,
1333
1.03k
                                             &ubx_sigid);
1334
1335
1.03k
        if (1 > ubx_svid) {
1336
           // skip bad PRN
1337
693
           GPSD_LOG(LOG_WARN, &session->context->errout,
1338
693
                    "ALLY: NAV-SVINFO bad svid %u\n", ally_svid);
1339
693
           continue;
1340
693
        }
1341
344
        nmea_PRN = ally_svid;
1342
        // pr Rate, m/s
1343
344
        session->gpsdata.skyview[st].prRate = getlef32((const char*)buf,
1344
344
                                                        off + 20);
1345
        // pseudorange, m
1346
344
        session->gpsdata.skyview[st].pr = getled64((const char*)buf, off + 24);
1347
1348
344
        session->gpsdata.skyview[st].PRN = nmea_PRN;
1349
344
        session->gpsdata.skyview[st].gnssid = ubx_gnssid;
1350
344
        session->gpsdata.skyview[st].svid = ubx_svid;
1351
1352
344
        session->gpsdata.skyview[st].ss = (double)cno;
1353
344
        if (90 >= abs(el)) {
1354
278
            session->gpsdata.skyview[st].elevation = (double)el;
1355
278
        }
1356
344
        if (359 > az &&
1357
278
            0 <= az) {
1358
195
            session->gpsdata.skyview[st].azimuth = (double)az;
1359
195
        }
1360
344
        session->gpsdata.skyview[st].prRes = prRes / 100.0;
1361
1362
        /* Undocumented, but (quality & 7) seems to be same as
1363
         * the u-blox qualityInd */
1364
344
        session->gpsdata.skyview[st].qualityInd = quality & 7;
1365
1366
        // No health data, no used data.
1367
        // flags and quality undocumented.
1368
344
        GPSD_LOG(LOG_PROG, &session->context->errout,
1369
344
                 "ALLY: NAV-SVINFO ally_svid %u  gnssid %d(%s) svid %d "
1370
344
                 "sigid %d flags x%x az %.0f el %.0f cno %.0f prRes %.2f "
1371
344
                 "quality %u, prRate %f pr %.4f\n",
1372
344
                 ally_svid,
1373
344
                 session->gpsdata.skyview[st].gnssid,
1374
344
                 val2str(session->gpsdata.skyview[st].gnssid, vgnssId),
1375
344
                 session->gpsdata.skyview[st].svid, ubx_sigid, flags,
1376
344
                 session->gpsdata.skyview[st].azimuth,
1377
344
                 session->gpsdata.skyview[st].elevation,
1378
344
                 session->gpsdata.skyview[st].ss,
1379
344
                 session->gpsdata.skyview[st].prRes,
1380
344
                 session->gpsdata.skyview[st].qualityInd,
1381
344
                 session->gpsdata.skyview[st].prRate,
1382
344
                 session->gpsdata.skyview[st].pr);
1383
        /* flags undocumented, here we assume same as UBX-NAV-SVINFO
1384
         * only  diffCorr,orbitAvail,orbitEphs seem to be used.
1385
         * how do we know if a dat is used in the fix?? */
1386
344
        GPSD_LOG(LOG_IO, &session->context->errout,
1387
344
                 "ALLY: NAV-SVINFO flags %ss\n",
1388
344
                 flags2str(flags, fsvinfo_flags, buf2, sizeof(buf2)));
1389
344
        st++;
1390
344
    }
1391
1392
    // session->gpsdata.satellites_visible = (int)st;
1393
    // session->gpsdata.satellites_used = (int)nsv;
1394
415
    GPSD_LOG(LOG_PROG, &session->context->errout,
1395
415
             "ALLY: NAV-SVINFO: visible=%d used=%d\n",
1396
415
             st, nsv);
1397
    // not ready for prime time, no sigid!!
1398
    // return SATELLITE_SET | USED_IS;
1399
415
    return 0;
1400
481
}
1401
1402
/**
1403
 * NAV-SVSTATE
1404
 */
1405
static gps_mask_t msg_nav_svstate(struct gps_device_t *session,
1406
                                  unsigned char *buf, size_t payload_len)
1407
1.34k
{
1408
1.34k
    unsigned idx;
1409
1.34k
    gps_mask_t mask = 0;
1410
    // char ts_buf[TIMESPEC_LEN];
1411
1412
1.34k
    unsigned numSV = getub(buf, 4);                // 32 bits?!?!
1413
1414
1.34k
    if (payload_len < (8 + (4 * numSV))) {
1415
        // length check
1416
239
        GPSD_LOG(LOG_WARN, &session->context->errout,
1417
239
                 "ALLY: NAV-SVSTATE: bad length %zd", payload_len);
1418
239
    }
1419
1.34k
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1420
    // 5 reserved
1421
1422
1.34k
    GPSD_LOG(LOG_PROG, &session->context->errout,
1423
1.34k
             "ALLY: NAV-SVSTATE: iTOW %lld numSV %u payload_len %lld\n",
1424
1.34k
             (long long)session->driver.ubx.iTOW, numSV,
1425
1.34k
             (long long) payload_len);
1426
30.7k
    for (idx = 8; idx < payload_len; idx += 4) {
1427
29.4k
        unsigned svid = getleu16(buf, idx);         // NMEA PRN
1428
29.4k
        unsigned eph_state = getub(buf, idx + 2);   // ephemeris state
1429
29.4k
        unsigned alm_state = getub(buf, idx + 3);   // ephemeris state
1430
29.4k
        unsigned ubx_gnssid, ubx_sigid;
1431
1432
29.4k
        unsigned ubx_svid = ally_svid_to_ids(session, svid, &ubx_gnssid,
1433
29.4k
                                             &ubx_sigid);
1434
1435
29.4k
        GPSD_LOG(LOG_PROG, &session->context->errout,
1436
29.4k
                 "ALLY: NAV-SVSTATE: svid %4u gnssid %u(%s) usvid %u sigid %u "
1437
29.4k
                 "eph x%x(use %d viz %u(%s) hlth %u(%s) "
1438
29.4k
                 "alm x%x(use %d asrc %u(%s) esrc %u(%s))\n",
1439
29.4k
                 svid, ubx_gnssid, val2str(ubx_gnssid, vgnssId),
1440
29.4k
                 ubx_svid, ubx_sigid,
1441
29.4k
                 eph_state, (eph_state >> 4) & 0x0f,
1442
29.4k
                 (eph_state >> 2) & 0x03,
1443
29.4k
                 val2str((eph_state >> 2) & 0x03, vsvstate_viz),
1444
29.4k
                 eph_state & 0x03,
1445
29.4k
                 val2str(eph_state & 0x03, vsvstate_hlth),
1446
29.4k
                 alm_state,
1447
29.4k
                 (alm_state >> 4) & 0x0f,
1448
29.4k
                 (alm_state >> 2) & 0x03,
1449
29.4k
                 val2str((alm_state >> 2) & 0x03, vsvstate_src),
1450
29.4k
                 alm_state & 0x03,
1451
29.4k
                 val2str(alm_state & 0x03, vsvstate_src));
1452
29.4k
    }
1453
1.34k
    return mask;
1454
1.34k
}
1455
1456
/**
1457
 * GPS Leap Seconds - NAV-TIME
1458
 * sorta like UBX-NAV-TIMEGPS
1459
 */
1460
static gps_mask_t msg_nav_time(struct gps_device_t *session,
1461
                               unsigned char *buf, size_t payload_len UNUSED)
1462
310
{
1463
310
    char buf2[80];
1464
310
    gps_mask_t mask = 0;
1465
310
    char ts_buf[TIMESPEC_LEN];
1466
1467
    // TAU1202 returns 21 ???
1468
310
    unsigned navSys = getub(buf, 0);               // which constellation
1469
310
    unsigned flag = getub(buf, 1);                 // Validity flags
1470
310
    int FracTow = getles16(buf, 2);                // fractional TOW, ns
1471
310
    unsigned week = getleu16(buf, 8);
1472
310
    int leapSec = getsb(buf, 10);
1473
    // timeErr in ns, unknown type (1 sigma, 50%, etc.)
1474
310
    double timeErr = (double)getleu32(buf, 12);
1475
1476
310
    session->driver.ubx.iTOW = getleu32(buf, 4);   // refTow, ms
1477
1478
    // Valid leap seconds ?
1479
310
    if (4 == (flag & 4)) {
1480
205
        session->context->leap_seconds = leapSec;
1481
205
        session->context->valid |= LEAP_SECOND_VALID;
1482
205
    }
1483
1484
    // Valid GPS time of week and week number
1485
310
    if (3 == (flag & 3)) {
1486
205
        timespec_t ts_tow;
1487
1488
205
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1489
205
        ts_tow.tv_nsec += FracTow;
1490
205
        TS_NORM(&ts_tow);
1491
205
        session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
1492
1493
205
        session->newdata.ept = timeErr / 1e9;
1494
205
        mask |= (TIME_SET | NTPTIME_IS);
1495
205
    }
1496
1497
310
    GPSD_LOG(LOG_PROG, &session->context->errout,
1498
310
             "ALLY: NAV-TIME: time=%s navSys %u flag x%x FracTow %d "
1499
310
             "refTow %lld week %u leapSec %d timeErr %g\n",
1500
310
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1501
310
             navSys, flag, FracTow,
1502
310
             (long long)session->driver.ubx.iTOW, week,
1503
310
             leapSec, timeErr);
1504
310
    GPSD_LOG(LOG_IO, &session->context->errout,
1505
310
             "ALLY: NAV-TIME: navSys %s flag:%s\n",
1506
310
       val2str(navSys, vtime_navsys),
1507
310
       flags2str(flag, vtime_flags, buf2, sizeof(buf2)));
1508
310
    return mask;
1509
310
}
1510
1511
/**
1512
 * NAV-TIMEUTC
1513
 */
1514
static gps_mask_t msg_nav_timeutc(struct gps_device_t *session,
1515
                                  unsigned char *buf, size_t payload_len UNUSED)
1516
66
{
1517
66
    char buf2[80];
1518
66
    gps_mask_t mask = 0;
1519
    // char ts_buf[TIMESPEC_LEN];
1520
1521
    // tAcc in ns, unknown type (1 sigma, 50%, etc.)
1522
66
    unsigned long long tAcc = getleu32(buf, 4);
1523
66
    long long nano = getles32(buf, 8);        // fractional TOW, ns
1524
66
    unsigned year = getleu16(buf, 12);
1525
66
    unsigned month = getub(buf, 14);
1526
66
    unsigned day = getub(buf, 15);
1527
66
    unsigned hour = getub(buf, 16);
1528
66
    unsigned min = getub(buf, 17);
1529
66
    unsigned sec = getub(buf, 18);
1530
66
    unsigned ValidFlag = getub(buf, 19);
1531
66
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1532
1533
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
1534
66
             "ALLY: NAV-TIMEUTC: iTOW %lld tAcc %llu nano %lld "
1535
66
             "time %u/%u/%u %u:%02u:%02u ValidFlag x%x\n",
1536
66
             (long long)session->driver.ubx.iTOW, tAcc, nano,
1537
66
             year, month, day, hour, min, sec, ValidFlag);
1538
             // timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1539
66
    GPSD_LOG(LOG_IO, &session->context->errout,
1540
66
             "ALLY: NAV-TIMEUTC: ValidFlag:x%x(%s) utc %s\n",
1541
66
             ValidFlag,
1542
66
             flags2str(ValidFlag, vtimeutc_valid, buf2, sizeof(buf2)),
1543
66
             val2str(ValidFlag, vtimeutc_utc));
1544
66
    return mask;
1545
66
}
1546
1547
/**
1548
 * NAV-VELECEF
1549
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1550
 */
1551
static gps_mask_t msg_nav_velecef(struct gps_device_t *session,
1552
                                  unsigned char *buf, size_t payload_len UNUSED)
1553
66
{
1554
66
    gps_mask_t mask = VECEF_SET;
1555
1556
66
    session->driver.ubx.iTOW = getleu32(buf, 0);              // iTow, ms
1557
66
    session->newdata.ecef.vx = getles32(buf, 4) / 100.0;      // cm/s
1558
66
    session->newdata.ecef.vy = getles32(buf, 8) / 100.0;      // cm/s
1559
66
    session->newdata.ecef.vz = getles32(buf, 12) / 100.0;     // cm/s
1560
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1561
66
    session->newdata.ecef.vAcc = getleu32(buf, 16) / 100.0;   // cm/s
1562
1563
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
1564
66
             "ALLY: NAV-VELECEF: iTOW %lld vECEF %.2f %.2f %.2f sAcc %.2f\n",
1565
66
             (long long)session->driver.ubx.iTOW,
1566
66
             session->newdata.ecef.vx,
1567
66
             session->newdata.ecef.vy,
1568
66
             session->newdata.ecef.vz,
1569
66
             session->newdata.ecef.vAcc);
1570
66
    return mask;
1571
66
}
1572
1573
/**
1574
 * NAV-VELNED
1575
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1576
 */
1577
static gps_mask_t msg_nav_velned(struct gps_device_t *session,
1578
                                 unsigned char *buf, size_t payload_len UNUSED)
1579
66
{
1580
66
    gps_mask_t mask = VNED_SET;
1581
1582
66
    session->driver.ubx.iTOW = getleu32(buf, 0);      // iTow, ms
1583
66
    session->newdata.NED.velN = getles32(buf, 4) / 100.0;
1584
66
    session->newdata.NED.velE = getles32(buf, 8) / 100.0;
1585
66
    session->newdata.NED.velD = getles32(buf, 12) / 100.0;
1586
1587
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1588
66
    session->newdata.ecef.vAcc = getleu32(buf, 28) / 100.0;   // cm/s
1589
1590
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
1591
66
             "ALLY: NAV-VELNED: iTOW %lld vNED %.2f %.2f %.2f sAcc %.2f\n",
1592
66
             (long long)session->driver.ubx.iTOW,
1593
66
             session->newdata.NED.velN,
1594
66
             session->newdata.NED.velE,
1595
66
             session->newdata.NED.velD,
1596
66
             session->newdata.ecef.vAcc);
1597
66
    return mask;
1598
66
}
1599
1600
/* RXM-*
1601
 * undocumented
1602
 * Class RXM, ID 0x01, payload 1, enables RXM-x57.
1603
 * Class RXM, ID 0x01, payload 0, disable RXM-x57.
1604
 *
1605
 * RXM-x57 undocumented.
1606
 */
1607
1608
/* msg_decode() -- dispatch all message types to proper decoder
1609
 */
1610
static gps_mask_t msg_decode(struct gps_device_t *session,
1611
                            unsigned char *buf, size_t payload_len)
1612
9.19k
{
1613
9.19k
    unsigned msgid = getbes16(buf, 2);
1614
9.19k
    gps_mask_t mask = 0;
1615
9.19k
    size_t needed_len;
1616
9.19k
    const char *msg_name;
1617
9.19k
    gps_mask_t (* p_decode)(struct gps_device_t *, unsigned char *, size_t);
1618
1619
9.19k
    switch (msgid) {
1620
241
    case ACK_NAK:
1621
241
        needed_len = 2;
1622
241
        msg_name = "ACK-NAK";
1623
241
        p_decode = msg_ack_nak;
1624
241
        break;
1625
100
    case ACK_ACK:
1626
100
        needed_len = 2;
1627
100
        msg_name = "ACK-ACK";
1628
100
        p_decode = msg_ack_ack;
1629
100
        break;
1630
0
    case CFG_ANTIJAM:
1631
0
        needed_len = 3;
1632
0
        msg_name = "CFG-ANTIJAM";
1633
0
        p_decode = msg_cfg_antijam;
1634
0
        break;
1635
0
    case CFG_CARRSMOOTH:
1636
0
        msg_name = "CFG-CARRSMOOTH";
1637
0
        needed_len = 1;
1638
0
        p_decode = msg_cfg_carrsmooth;
1639
0
        break;
1640
0
    case CFG_GEOFENCE:
1641
0
        msg_name = "CFG-GEOFENCE";
1642
0
        needed_len = 8;
1643
0
        p_decode = msg_cfg_geofence;
1644
0
        break;
1645
0
    case CFG_NAVSAT:
1646
0
        needed_len = 4;
1647
0
        msg_name = "CFG-NAVSAT";
1648
0
        p_decode = msg_cfg_navsat;
1649
0
        break;
1650
0
    case CFG_NMEAVER:
1651
0
        msg_name = "CFG-NMEAVER";
1652
0
        needed_len = 1;
1653
0
        p_decode = msg_cfg_nmeaver;
1654
0
        break;
1655
0
    case CFG_PPS:
1656
0
        msg_name = "CFG-PPS";
1657
0
        needed_len = 5;
1658
0
        p_decode = msg_cfg_pps;
1659
0
        break;
1660
66
    case CFG_PRT:
1661
66
        msg_name = "CFG-PRT";
1662
66
        needed_len = 2;
1663
66
        p_decode = msg_cfg_prt;
1664
66
        break;
1665
0
    case CFG_SBAS:
1666
0
        msg_name = "CFG-SBAS";
1667
0
        needed_len = 0;
1668
0
        p_decode = msg_cfg_sbas;
1669
0
        break;
1670
604
    case MON_VER:
1671
604
        msg_name ="MON-VER";
1672
604
        needed_len = 32;
1673
604
        p_decode = msg_mon_ver;
1674
604
        break;
1675
268
    case NAV_AUTO:
1676
268
        msg_name ="NAV-AUTO";
1677
268
        needed_len = 32;
1678
268
        p_decode = msg_nav_auto;
1679
268
        break;
1680
132
    case NAV_CLOCK:
1681
132
        msg_name ="NAV-CLOCK";
1682
132
        needed_len = 20;
1683
132
        p_decode = msg_nav_clock;
1684
132
        break;
1685
192
    case NAV_CLOCK2:
1686
192
        msg_name ="NAV-CLOCK2";
1687
192
        needed_len = 8;
1688
192
        p_decode = msg_nav_clock2;
1689
192
        break;
1690
552
    case NAV_DOP:
1691
552
        msg_name ="NAV-DOP";
1692
552
        needed_len = 18;
1693
552
        p_decode = msg_nav_dop;
1694
552
        break;
1695
155
    case NAV_POSECEF:
1696
155
        msg_name ="NAV-POSECEF";
1697
155
        needed_len = 20;
1698
155
        p_decode = msg_nav_posecef;
1699
155
        break;
1700
210
    case NAV_POSLLH:
1701
210
        msg_name ="NAV-POSLLH";
1702
210
        needed_len = 28;
1703
210
        p_decode = msg_nav_posllh;
1704
210
        break;
1705
69
    case NAV_PVERR:
1706
69
        msg_name ="NAV-PVERR";
1707
69
        needed_len = 28;
1708
69
        p_decode = msg_nav_pverr;
1709
69
        break;
1710
401
    case NAV_PVT:
1711
401
        msg_name ="NAV-PVT";
1712
401
        needed_len = 88;
1713
401
        p_decode = msg_nav_pvt;
1714
401
        break;
1715
613
    case NAV_SVINFO:
1716
613
        msg_name ="NAV-SVINFO";
1717
613
        needed_len = 8;
1718
613
        p_decode = msg_nav_svinfo;
1719
613
        break;
1720
1.40k
    case NAV_SVSTATE:
1721
1.40k
        msg_name ="NAV-SVSTATE";
1722
1.40k
        needed_len = 8;
1723
1.40k
        p_decode = msg_nav_svstate;
1724
1.40k
        break;
1725
310
    case NAV_TIME:
1726
310
        msg_name ="NAV-TIME";
1727
310
        needed_len = 16;
1728
310
        p_decode = msg_nav_time;
1729
310
        break;
1730
132
    case NAV_TIMEUTC:
1731
132
        msg_name ="NAV-TIMEUTC";
1732
132
        needed_len = 20;
1733
132
        p_decode = msg_nav_timeutc;
1734
132
        break;
1735
137
    case NAV_VELECEF:
1736
137
        msg_name ="NAV-VELECEF";
1737
137
        needed_len = 20;
1738
137
        p_decode = msg_nav_velecef;
1739
137
        break;
1740
176
    case NAV_VELNED:
1741
176
        msg_name ="NAV-VELNED";
1742
176
        needed_len = 36;
1743
176
        p_decode = msg_nav_velned;
1744
176
        break;
1745
3.42k
    default:
1746
3.42k
        msg_name ="NAV-unk";
1747
3.42k
        needed_len = 0;
1748
3.42k
        p_decode = NULL;
1749
3.42k
        break;
1750
9.19k
    }
1751
9.19k
    if (needed_len > payload_len) {
1752
1.67k
        GPSD_LOG(LOG_WARN, &session->context->errout,
1753
1.67k
                 "ALLY: %s: runt payload len %zd need %zd\n",
1754
1.67k
                 msg_name, payload_len, needed_len);
1755
1.67k
        return 0;
1756
1.67k
    }
1757
7.51k
    if (NULL == p_decode) {
1758
3.42k
        GPSD_LOG(LOG_WARN, &session->context->errout,
1759
3.42k
                 "ALLY: Unknown %02x)%s)-%02x payload_len %zd\n",
1760
3.42k
                 (msgid >> 8) & 0xff,
1761
3.42k
                 val2str((msgid >> 8) & 0xff, vclass),
1762
3.42k
                 msgid & 0xff, payload_len);
1763
3.42k
        return 0;
1764
3.42k
    }
1765
4.08k
    mask = p_decode(session, &buf[ALLY_PREFIX_LEN], payload_len);
1766
4.08k
    return mask;
1767
7.51k
}
1768
1769
static gps_mask_t ally_parse(struct gps_device_t * session, unsigned char *buf,
1770
                            size_t len)
1771
9.41k
{
1772
9.41k
    size_t payload_len;
1773
9.41k
    gps_mask_t mask = 0;
1774
1775
    /* the packet at least 8 bytes / Min packet is 8 ==  header (2),
1776
    *  Message ID (2), length (2) and checksum (2).  The packetizer should
1777
    *  already guarantee, This is to protect against malicious fuzzing. */
1778
9.41k
    if (8 > len) {
1779
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1780
0
                 "ALLY: runt message len %zu\n", len);
1781
0
        return 0;
1782
0
    }
1783
1784
    // session->cycle_end_reliable = true; // Not yet.
1785
    // for now, use driver.ubx.
1786
9.41k
    session->driver.ubx.iTOW = -1;        // set by decoder
1787
1788
    // extract payload length, check against actual length
1789
9.41k
    payload_len = getles16(buf, 4);
1790
1791
9.41k
    if ((len - 8) != payload_len) {
1792
225
        GPSD_LOG(LOG_WARN, &session->context->errout,
1793
225
                 "ALLY: len (%zu) does not match payload (%zu) + 8\n",
1794
225
                 len, payload_len);
1795
225
        return 0;
1796
225
    }
1797
1798
9.19k
    mask = msg_decode(session, buf, payload_len);
1799
1800
    /* handle the init queue.  Some parts get cranky when they
1801
     * get too many configuration changes at once.
1802
     */
1803
9.19k
    if (!session->context->readonly &&
1804
6.67k
        0 <= session->queue &&
1805
6.67k
        100 > session->queue) {
1806
6.38k
        unsigned char msg[4] = {0};
1807
1808
6.38k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1809
6.38k
                 "ALLY: queue %d\n", session->queue);
1810
1811
1812
6.38k
        switch (session->queue) {
1813
169
        case 10:
1814
            // start at 10 so the initial configuration can finish first.
1815
            // Poll CFG-ANTIJAM
1816
169
            (void)ally_write(session, ALLY_CFG, 0x15, NULL, 0);
1817
169
            break;
1818
145
        case 14:
1819
            // Poll CFG_CARRSMOOTH
1820
145
            (void)ally_write(session, ALLY_CFG, 0x17, NULL, 0);
1821
145
            break;
1822
130
        case 18:
1823
            // Poll CFG_GEOFENCE
1824
130
            (void)ally_write(session, ALLY_CFG, 0x18, NULL, 0);
1825
130
            break;
1826
116
        case 22:
1827
            // Poll CFG-NAVSAT
1828
116
            (void)ally_write(session, ALLY_CFG, 0x0c, NULL, 0);
1829
116
            break;
1830
94
        case 26:
1831
            // Poll CFG-NMEAVER
1832
94
            (void)ally_write(session, ALLY_CFG, 0x43, NULL, 0);
1833
94
            break;
1834
76
        case 30:
1835
            // Poll CFG-PRT
1836
76
            (void)ally_write(session, ALLY_CFG, 0x00, NULL, 0);
1837
76
            break;
1838
72
        case 34:
1839
            // Poll CFG-PRT(UART1)
1840
72
            msg[0] = 0;
1841
72
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1842
72
            break;
1843
63
        case 38:
1844
            // Poll CFG-PRT(UART2)
1845
63
            msg[0] = 1;
1846
63
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1847
63
            break;
1848
58
        case 40:
1849
            // Poll CFG-SBAS
1850
58
            (void)ally_write(session, ALLY_CFG, 0x0e, NULL, 0);
1851
58
            break;
1852
46
        case 44:
1853
#if 0       // Debug code
1854
            // turn off RXM-0x01
1855
            msg[0] = 0x00;          // enable?
1856
            (void)ally_write(session, ALLY_RXM, 0x01, msg, 1);
1857
#endif
1858
46
            break;
1859
5.41k
        default:
1860
5.41k
            break;
1861
6.38k
        }
1862
6.38k
        session->queue++;
1863
6.38k
    }
1864
1865
9.19k
    return mask;
1866
9.19k
}
1867
1868
static gps_mask_t parse_input(struct gps_device_t *session)
1869
9.41k
{
1870
9.41k
    if (ALLYSTAR_PACKET == session->lexer.type) {
1871
9.41k
        return ally_parse(session, session->lexer.outbuffer,
1872
9.41k
                          session->lexer.outbuflen);
1873
9.41k
    }
1874
    // a comment, JSON, or NMEA 0183
1875
0
    return generic_parse_input(session);
1876
9.41k
}
1877
1878
// not used by gpsd, it's for gpsctl and friends
1879
static ssize_t control_send(struct gps_device_t *session, char *msg,
1880
                            size_t data_len)
1881
0
{
1882
0
    return ally_write(session, (unsigned int)msg[0], (unsigned int)msg[1],
1883
0
                      (unsigned char *)msg + 2,
1884
0
                      (size_t)(data_len - 2)) ? ((ssize_t) (data_len + 7)) : -1;
1885
0
}
1886
1887
// ally_mode(), stub for NMEA/BINARY changer.
1888
static void ally_mode(struct gps_device_t *session, int mode UNUSED)
1889
415
{
1890
415
    unsigned char msg[4] = {0};
1891
415
    unsigned u;
1892
1893
    // TAU1202 seems OK with being blasted with CFG-MSG's.
1894
415
    static ally_msgs_t all_nav[] = {
1895
        // prolly no need for NAV-AUTO and NAV-POLL
1896
415
        NAV_AUTO,
1897
415
        NAV_CLOCK,
1898
        // NAV_CLOCK2,           // unsupported TAU1202
1899
415
        NAV_DOP,
1900
415
        NAV_POSECEF,
1901
415
        NAV_POSLLH,
1902
        // NAV_PVERR,            // unsupported TAU1202
1903
        // NAV_PVT,              // unsupported TAU1202
1904
415
        NAV_SVINFO,
1905
415
        NAV_SVSTATE,
1906
415
        NAV_TIME,
1907
415
        NAV_TIMEUTC,
1908
        // NAV_VELECEF,          // unsupported TAU1202
1909
        // NAV_VELNED,           // unsupported TAU1202
1910
415
    };
1911
1912
    // turn on all binary NAV- messages we know of
1913
4.15k
    for (u = 0; u < ROWS(all_nav); u++) {
1914
        // turn on rate one
1915
3.73k
        putbe16(msg, 0, all_nav[u]);
1916
3.73k
        msg[2] = 0x01;          // rate, one
1917
3.73k
        (void)ally_write(session, ALLY_CFG, 0x01, msg, 3);
1918
3.73k
    }
1919
415
}
1920
1921
/* speed()
1922
 * Luckily ALLYSTAR is only 8N1.
1923
 * Unluckily, no way to know what port we are on.
1924
 * So we assume UART0
1925
 */
1926
static bool speed(struct gps_device_t *session, speed_t speed,
1927
                  char parity UNUSED, int stopbits UNUSED)
1928
0
{
1929
0
    unsigned char msg[8] = {0};
1930
1931
0
    memset(msg, '\0', sizeof(msg));
1932
1933
0
    GPSD_LOG(LOG_WARN, &session->context->errout,
1934
0
             "ALLY: baudrate %u\n", (unsigned int) speed);
1935
1936
0
    if (0 == speed) {
1937
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1938
0
                 "ALLY: invalid baudrate %u\n", (unsigned int) speed);
1939
0
        return 0;
1940
0
    }
1941
    // set CFG-PRT for UART0
1942
0
    msg[0] = 0x00;          // assume: UART0
1943
    // 1 to 3, reserved.
1944
0
    putle32(msg, 4, speed);
1945
0
    (void)ally_write(session, ALLY_CFG, CFG_PRT & 0x0ff, msg, 8);
1946
1947
0
    if ((speed_t)0 != session->context->fixed_port_speed) {
1948
        // save new speed as the fixed speed
1949
0
        session->context->fixed_port_speed = speed;
1950
0
    }
1951
    // change port speed to new speed.  Allystar is always 8N1
1952
0
    gpsd_set_speed(session, speed, 'N', 1);
1953
1954
0
    return true;
1955
0
}
1956
1957
static void event_hook(struct gps_device_t *session, event_t event)
1958
13.9k
{
1959
13.9k
    if (session->context->readonly) {
1960
3.58k
        return;
1961
3.58k
    }
1962
10.4k
    if (event == EVENT_IDENTIFIED) {
1963
514
        GPSD_LOG(LOG_PROG, &session->context->errout, "ALLY: identified\n");
1964
1965
        // no longer set UBX-CFG-SBAS here, u-blox 9 and 10 do not have it
1966
1967
514
        if (session->context->passive) {
1968
            /* passive mode, do no autoconfig
1969
             * but we really want MON-VER. */
1970
99
            (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1971
415
        } else if (O_OPTIMIZE == session->mode) {
1972
0
            ally_mode(session, MODE_BINARY);
1973
            // restart init queue
1974
0
            session->queue = 0;
1975
415
        } else {
1976
            //* Turn off NMEA output, turn on UBX on this port.
1977
415
            ally_mode(session, MODE_NMEA);
1978
415
        }
1979
9.88k
    } else if (event == EVENT_DEACTIVATE) {
1980
        /* There used to be a hotstart/reset here.
1981
         * That caused u-blox USB to re-enumerate.
1982
         * Sometimes to a new device name.
1983
         * Bad.  Don't do that anymore...
1984
         */
1985
0
    }
1986
10.4k
}
1987
1988
static void init_query(struct gps_device_t *session)
1989
578
{
1990
    // MON-VER: query for version information
1991
    (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1992
578
}
1993
1994
// this is everything we export
1995
// *INDENT-OFF*
1996
const struct gps_type_t driver_allystar =
1997
{
1998
    .type_name      = "ALLYSTAR",               // full name of type
1999
    .packet_type    = ALLYSTAR_PACKET,          // lexer packet type
2000
    .flags          = DRIVER_STICKY,            // remember this
2001
    .trigger        = NULL,                     // recognize the type
2002
    .channels       = 240,                      //  a guess
2003
    .probe_detect   = NULL,                     // no probe
2004
    .get_packet     = packet_get1,              // use generic one
2005
    .parse_packet   = parse_input,              // parse message packets
2006
    .rtcm_writer    = gpsd_write,               // send RTCM data straight
2007
    .init_query     = init_query,               // non-perturbing query
2008
    .event_hook     = event_hook,               // lifetime event handler
2009
    .speed_switcher = speed,                    // we can change baud rates
2010
    .mode_switcher  = NULL,                     // there is a mode switcher
2011
    .rate_switcher  = NULL,                     // change sample rate
2012
    .min_cycle.tv_sec  = 1,                     // default
2013
    .min_cycle.tv_nsec = 0,                     // default
2014
    .control_send   = control_send,             // how to send a control string
2015
    .time_offset    = NULL,                     // no NTP fudge factor
2016
};
2017
// *INDENT-ON*
2018
2019
// vim: set expandtab shiftwidth=4