Coverage Report

Created: 2026-06-13 07:00

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
0
#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
0
{
315
0
    unsigned char CK_A, CK_B;
316
0
    ssize_t count;
317
0
    size_t i;
318
0
    bool ok;
319
320
    // do not write if -b (readonly) option set
321
    // "passive" handled earlier
322
0
    if (session->context->readonly) {
323
0
        return true;
324
0
    }
325
326
0
    session->msgbuf[0] = 0xf1;
327
0
    session->msgbuf[1] = 0xd9;
328
329
0
    CK_A = CK_B = 0;
330
0
    session->msgbuf[2] = msg_class;
331
0
    session->msgbuf[3] = msg_id;
332
0
    session->msgbuf[4] = payload_len & 0xff;
333
0
    session->msgbuf[5] = (payload_len >> 8) & 0xff;
334
335
0
    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
0
    if (NULL != msg &&
341
0
        0 < payload_len) {
342
0
        (void)memcpy(&session->msgbuf[ALLY_PREFIX_LEN], msg, payload_len);
343
0
    }
344
345
    // calculate CRC
346
0
    for (i = 2; i < (6 + payload_len); i++) {
347
0
        CK_A += session->msgbuf[i];
348
0
        CK_B += CK_A;
349
0
    }
350
351
0
    session->msgbuf[6 + payload_len] = CK_A;
352
0
    session->msgbuf[7 + payload_len] = CK_B;
353
0
    session->msgbuflen = payload_len + 8;
354
355
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
356
0
             "=> GPS: ALLY: class: %02x, id: %02x, len: %zd, crc: %02x%02x\n",
357
0
             msg_class, msg_id, payload_len,
358
0
             CK_A, CK_B);
359
0
    count = gpsd_write(session, session->msgbuf, session->msgbuflen);
360
0
    ok = (count == (ssize_t) session->msgbuflen);
361
0
    return ok;
362
0
}
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
0
{
375
0
    unsigned ubx_svid = 0;
376
0
    *ubx_gnssid = 0;
377
0
    *ubx_sigid = 0;
378
379
0
    if (IN(1, svid, 32)) {
380
        // GPS
381
0
        *ubx_gnssid = 0;
382
0
        *ubx_sigid = 0;
383
0
        ubx_svid = svid;
384
0
    } else if (IN(40, svid, 54)) {
385
        // SBAS 40-54, not 40-58??
386
0
        *ubx_gnssid = 1;
387
0
        *ubx_sigid = 0;
388
0
        ubx_svid = svid + 80;
389
0
    } else if (IN(65, svid, 96)) {
390
        // GLONASS G1 OF
391
0
        *ubx_gnssid = 6;
392
0
        *ubx_sigid = 0;
393
0
        ubx_svid = svid;
394
0
    } else if (IN(127, svid, 141)) {
395
        // SBAS, 127-151, not 120-158?
396
0
        *ubx_gnssid = 1;
397
0
        *ubx_sigid = 0;
398
0
        ubx_svid = svid;
399
0
    } else if (IN(193, svid, 199)) {
400
        // QZSS L1 C/A
401
0
        *ubx_gnssid = 5;
402
0
        *ubx_sigid = 0;
403
0
        ubx_svid = svid - 192;
404
0
    } else if (IN(201, svid, 263)) {
405
        // BDS B1
406
0
        *ubx_gnssid = 3;
407
0
        *ubx_sigid = 0;
408
0
        ubx_svid = svid - 200;
409
0
    } else if (IN(301, svid, 336)) {
410
        // Galileo E1 C
411
0
        *ubx_gnssid = 2;
412
0
        *ubx_sigid = 0;
413
0
        ubx_svid = svid - 300;
414
0
    } else if (IN(401, svid, 432)) {
415
        // GPS L1C NMEA sig 9
416
0
        *ubx_gnssid = 0;
417
0
        *ubx_sigid = 0;    // wrong...
418
0
        ubx_svid = svid - 400;
419
0
    } else if (IN(501, svid, 532)) {
420
        // GPS L2CM
421
0
        *ubx_gnssid = 2;
422
0
        *ubx_sigid = 4;
423
0
        ubx_svid = svid - 500;
424
0
    } else if (IN(565, svid, 596)) {
425
        // GLONASS G2
426
0
        *ubx_gnssid = 6;
427
0
        *ubx_sigid = 2;
428
0
        ubx_svid = svid - 564;
429
0
    } else if (IN(601, svid, 663)) {
430
        // BDS B1C, overlaps GPS L6, 651-682!
431
0
        *ubx_gnssid = 3;
432
0
        *ubx_sigid = 5;      // 5 or 6??
433
0
        ubx_svid = svid - 600;
434
0
    } else if (IN(651, svid, 682)) {
435
        // GPS L5, overlaps BDS B1C 601-663
436
0
        *ubx_gnssid = 0;
437
0
        *ubx_sigid = 0;      // wrong....
438
0
        ubx_svid = svid - 650;
439
0
    } else if (IN(701, svid, 763)) {
440
        // BDS B1I
441
0
        *ubx_gnssid = 3;
442
0
        *ubx_sigid = 1;      // could be 0 or 1?
443
0
        ubx_svid = svid - 700;
444
0
    } else if (IN(801, svid, 863)) {
445
        // BDS B3I, overlaps BDS B2A, 851-914
446
0
        *ubx_gnssid = 3;
447
0
        *ubx_sigid = 2;      // wrong....
448
0
        ubx_svid = svid - 800;
449
0
    } 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
0
    } else if (IN(851, svid, 913)) {
458
        // BDS B2A - overlaps BDS B3I 801-863, IRNSS L5 901-918
459
0
        *ubx_gnssid = 3;
460
0
        *ubx_sigid = 7;      // ??
461
0
        ubx_svid = svid - 850;
462
0
    } 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
0
    } else if (IN(951, svid, 986)) {
468
        // GAL E5A
469
0
        *ubx_gnssid = 2;
470
0
        *ubx_sigid = 3;      // Could be 3 or 4?
471
0
        ubx_svid = svid - 950;
472
0
    } else {
473
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
474
0
                 "ALLY:ally_svid_to_ids(%u) unknown svid\n", svid);
475
0
        *ubx_gnssid = 0;
476
0
        ubx_svid = 0;
477
0
    }
478
0
    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
0
    return ubx_svid;
488
0
}
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
0
{
496
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
497
0
             "ALLY: ACK-ACK: class: %02x(%s), id: %02x\n",
498
0
              buf[0], val2str(buf[0], vclass), buf[1]);
499
0
    return 0;
500
0
}
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
0
{
506
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
507
0
             "ALLY: ACK-NAK: class: %02x(%s), id: %02x\n",
508
0
              buf[0], val2str(buf[0], vclass), buf[1]);
509
0
    return 0;
510
0
}
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
0
{
671
0
    unsigned portID  = getub(buf, 0);
672
0
    unsigned long long baudrate = getleu32(buf, 4);
673
674
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
675
0
             "ALLY: CFG-PRT: portID %u(%s) baudrate %llu\n",
676
0
             portID, val2str(portID, vportID), baudrate);
677
678
0
    return 0;
679
0
}
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
0
{
721
    // save SW and HW Version as subtype
722
0
    (void)snprintf(session->subtype, sizeof(session->subtype),
723
0
                   "SW %.16s,HW %.16s",
724
0
                   (char *)buf,
725
0
                   (char *)(buf + 16));
726
727
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
728
0
             "ALLY: MON-VER: %s\n",
729
0
             session->subtype);
730
731
0
    return 0;
732
0
}
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
0
{
743
0
    gps_mask_t mask = 0;
744
0
    unsigned mode = MODE_NOT_SEEN;
745
0
    unsigned status = STATUS_UNK;
746
0
    struct tm unpacked_date = {0};
747
748
0
    unsigned fixstate = getub(buf, 0);
749
0
    unsigned year = getleu16(buf, 1);
750
0
    unsigned month = getub(buf, 3);
751
0
    unsigned day = getub(buf, 4);
752
0
    unsigned hour = getub(buf, 5);
753
0
    unsigned min = getub(buf, 6);
754
0
    unsigned sec = getub(buf, 7);
755
0
    long long lon = getles32(buf, 8);
756
0
    long long lat = getles32(buf, 12);
757
0
    long long altHAE = getles32(buf, 16);
758
    // no place for speed_3d....
759
0
    unsigned speed_3d = getleu16(buf, 20);
760
0
    int heading = getles16(buf, 22);
761
0
    unsigned pDOP = getles16(buf, 24);
762
0
    unsigned hDOP = getles16(buf, 26);
763
0
    unsigned vDOP = getles16(buf, 28);
764
    /* We don't use satInUse, satInView.  ALLYSTAR counts two signals from
765
     * one sat, as one sat */
766
0
    unsigned satInUse = getub(buf, 30);
767
0
    unsigned satInView = getub(buf, 31);
768
769
0
    switch (fixstate) {
770
0
    case 0:    // No fix
771
0
        mode |= MODE_NO_FIX;
772
0
        status = STATUS_UNK;
773
0
        mask |= STATUS_SET | MODE_SET;
774
0
        break;
775
0
    case 1:    // Aided fix ??
776
0
        FALLTHROUGH
777
0
    case 2:    // Clock bias fix ??
778
        // assume these are like surveyed-in
779
0
        mode = MODE_3D;
780
0
        status = STATUS_TIME;
781
0
        mask |= STATUS_SET | MODE_SET;
782
0
        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
0
    default:
809
        // huh?
810
0
        break;
811
0
    }
812
0
    session->newdata.mode = mode;
813
0
    session->newdata.status = status;
814
815
    // We don't know if time, or leapseconds, is valid.
816
0
    unpacked_date.tm_year = year - 1900;
817
0
    unpacked_date.tm_mon = month - 1;
818
0
    unpacked_date.tm_mday = day;
819
0
    unpacked_date.tm_hour = hour;
820
0
    unpacked_date.tm_min = min;
821
0
    unpacked_date.tm_sec = sec;
822
0
    session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
823
    // If rate higher than 1Hz, we have no idea of sub-seconds...
824
0
    session->newdata.time.tv_nsec = 0;
825
0
    TS_NORM(&session->newdata.time);
826
0
    if (3 <= fixstate) {
827
        // not sure about states 1 and 2
828
0
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
829
0
    }
830
831
0
    session->newdata.longitude = lon / 1e7;
832
0
    session->newdata.latitude = lat / 1e7;
833
0
    session->newdata.altHAE = altHAE / 1e3;
834
835
0
    session->newdata.track = heading / 100.0;
836
0
    mask |= TRACK_SET;
837
838
0
    if (9999 > pDOP) {
839
0
        session->gpsdata.dop.pdop = pDOP / 100.0;
840
0
        mask |= DOP_SET;
841
0
    }
842
0
    if (9999 > hDOP) {
843
0
        session->gpsdata.dop.hdop = hDOP / 100.0;
844
0
        mask |= DOP_SET;
845
0
    }
846
0
    if (9999 > vDOP) {
847
0
        session->gpsdata.dop.vdop = vDOP / 100.0;
848
0
        mask |= DOP_SET;
849
0
    }
850
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
851
0
             "ALLY: NAV-AUTO: time %lld fixstate %u %u/%u/%u "
852
0
             "%u:%02u:%02u lon %.7f lat %.7f altHAE %.3f "
853
0
             "speed_3d %u, heading %.2f pDOP %.2f hDOP %.2f vDOP %.2f "
854
0
             "satInUse %u satInView %u\n",
855
0
             (long long)session->newdata.time.tv_sec,
856
0
             fixstate,
857
0
             year, month, day, hour, min, sec,
858
0
             session->newdata.longitude,
859
0
             session->newdata.latitude,
860
0
             session->newdata.altHAE,
861
0
             speed_3d,
862
0
             session->newdata.track,
863
0
             session->gpsdata.dop.pdop,
864
0
             session->gpsdata.dop.hdop,
865
0
             session->gpsdata.dop.vdop,
866
0
             satInUse, satInView);
867
0
     GPSD_LOG(LOG_IO, &session->context->errout,
868
0
              "ALLY: NAV-SUTO: fixstate %u(%s) mode %u(%s) status %u(%s)\n",
869
0
             fixstate, val2str(fixstate, vfixstate),
870
0
             session->newdata.mode,
871
0
             val2str(session->newdata.mode, vmode_str),
872
0
             session->newdata.status,
873
0
             val2str(session->newdata.status, vstatus_str));
874
0
    return mask;
875
0
}
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
0
{
885
0
    unsigned long tAcc = getleu32(buf, 12);
886
0
    unsigned long fAcc = getleu32(buf, 16);
887
888
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
889
0
    session->gpsdata.fix.clockbias = getles32(buf, 4);
890
0
    session->gpsdata.fix.clockdrift = getles32(buf, 8);
891
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
892
0
             "ALLY: NAV-CLOCK: iTOW=%lld clkB %ld clkD %ld tAcc %lu fAcc %lu\n",
893
0
             (long long)session->driver.ubx.iTOW,
894
0
             session->gpsdata.fix.clockbias,
895
0
             session->gpsdata.fix.clockdrift,
896
0
             tAcc, fAcc);
897
0
    return 0;
898
0
}
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
0
{
908
0
    char buf2[80];
909
0
    unsigned u;
910
911
0
    unsigned long long numClk = getleu32(buf, 4);
912
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
913
914
0
    for (u = 8; u < payload_len; u += 12) {
915
0
        unsigned long long sysmask = getleu32(buf, u);
916
0
        long long clkB = getles32(buf, u + 4);              // ns
917
0
        unsigned long long tAcc = getleu16(buf, u + 8);     // ns
918
919
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
920
0
                 "ALLY: CFG-CLOCK2: sysmask x%llx(%s) clkB %lld yAcc %llu\n",
921
0
                 sysmask,
922
0
                 flags2str(sysmask, fenableMask, buf2, sizeof(buf2)),
923
0
                 clkB, tAcc);
924
0
    }
925
926
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
927
0
             "ALLY: NAV-CLOCK2: iTOW=%lld numClk %lld\n",
928
0
             (long long)session->driver.ubx.iTOW,
929
0
             numClk);
930
0
    return 0;
931
0
}
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
0
{
941
0
    unsigned u;
942
0
    gps_mask_t mask = 0;
943
944
0
    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
0
    u = getleu16(buf, 4);
952
0
    if (9999 > u) {
953
0
        session->gpsdata.dop.gdop = (double)(u / 100.0);
954
0
        mask |= DOP_SET;
955
0
    }
956
0
    u = getleu16(buf, 6);
957
0
    if (9999 > u) {
958
0
        session->gpsdata.dop.pdop = (double)(u / 100.0);
959
0
        mask |= DOP_SET;
960
0
    }
961
0
    u = getleu16(buf, 8);
962
0
    if (9999 > u) {
963
0
        session->gpsdata.dop.tdop = (double)(u / 100.0);
964
0
        mask |= DOP_SET;
965
0
    }
966
0
    u = getleu16(buf, 10);
967
0
    if (9999 > u) {
968
0
        session->gpsdata.dop.vdop = (double)(u / 100.0);
969
0
        mask |= DOP_SET;
970
0
    }
971
0
    u = getleu16(buf, 12);
972
0
    if (9999 > u) {
973
0
        session->gpsdata.dop.hdop = (double)(u / 100.0);
974
0
        mask |= DOP_SET;
975
0
    }
976
    // Northing DOP
977
0
    u = getleu16(buf, 14);
978
0
    if (9999 > u) {
979
0
        session->gpsdata.dop.ydop = (double)(u / 100.0);
980
0
        mask |= DOP_SET;
981
0
    }
982
    // Easting DOP
983
0
    u = getleu16(buf, 16);
984
0
    if (9999 > u) {
985
0
        session->gpsdata.dop.xdop = (double)(u / 100.0);
986
0
        mask |= DOP_SET;
987
0
    }
988
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
989
0
             "ALLY: NAV-DOP: gdop=%.2f pdop=%.2f "
990
0
             "hdop=%.2f vdop=%.2f tdop=%.2f ydop=%.2f xdop=%.2f\n",
991
0
             session->gpsdata.dop.gdop,
992
0
             session->gpsdata.dop.hdop,
993
0
             session->gpsdata.dop.vdop,
994
0
             session->gpsdata.dop.pdop, session->gpsdata.dop.tdop,
995
0
             session->gpsdata.dop.ydop, session->gpsdata.dop.xdop);
996
0
    return mask;
997
0
}
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
0
{
1008
0
    gps_mask_t mask = ECEF_SET;
1009
1010
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
1011
    // all in cm
1012
0
    session->newdata.ecef.x = getles32(buf, 4) / 100.0;
1013
0
    session->newdata.ecef.y = getles32(buf, 8) / 100.0;
1014
0
    session->newdata.ecef.z = getles32(buf, 12) / 100.0;
1015
0
    session->newdata.ecef.pAcc = getleu32(buf, 16) / 100.0;
1016
1017
    // (long long) cast for 32-bit compat
1018
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1019
0
        "ALLY: NAV-POSECEF: iTOW=%lld ECEF x=%.2f y=%.2f z=%.2f pAcc=%.2f\n",
1020
0
        (long long)session->driver.ubx.iTOW,
1021
0
        session->newdata.ecef.x,
1022
0
        session->newdata.ecef.y,
1023
0
        session->newdata.ecef.z,
1024
0
        session->newdata.ecef.pAcc);
1025
0
    return mask;
1026
0
}
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
0
{
1040
0
    gps_mask_t mask = 0;
1041
1042
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
1043
0
    session->newdata.longitude = getles32(buf, 4) / 1e7;
1044
0
    session->newdata.latitude = getles32(buf, 8) / 1e7;
1045
    // altitude WGS84
1046
0
    session->newdata.altHAE = getles32(buf, 12) / 1e3;
1047
    // altitude MSL
1048
0
    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
0
    session->newdata.eph = getleu32(buf, 20) / 1e3;
1053
    // Vertical accuracy estimate in mm, unknown type
1054
0
    session->newdata.epv = getleu32(buf, 24) / 1e3;
1055
1056
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1057
0
        "ALLY: NAV-POSLLH: iTOW=%lld lat=%.3f lon=%.3f altHAE=%.3f "
1058
0
        "eph %.3f epv %.3f\n",
1059
0
        (long long)session->driver.ubx.iTOW,
1060
0
        session->newdata.latitude,
1061
0
        session->newdata.longitude,
1062
0
        session->newdata.altHAE,
1063
0
        session->newdata.eph,
1064
0
        session->newdata.epv);
1065
1066
0
    mask = ONLINE_SET | HERR_SET | VERR_SET | LATLON_SET | ALTITUDE_SET;
1067
0
    return mask;
1068
0
}
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
0
{
1081
0
    gps_mask_t mask = 0;
1082
1083
    // Probably NOT fix time
1084
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
1085
    // latitude, millimeters
1086
0
    session->gpsdata.gst.lat_err_deviation = getles32(buf, 4)/ 1000.0;
1087
    // longitude, millimeters
1088
0
    session->gpsdata.gst.lon_err_deviation = getles32(buf, 4)/ 1000.0;
1089
    // altitude, millimeters
1090
0
    session->gpsdata.gst.alt_err_deviation = getles32(buf, 4)/ 1000.0;
1091
    // velocity east, millimeters / second
1092
0
    session->gpsdata.gst.ve_err_deviation  = getles32(buf, 4)/ 1000.0;
1093
    // velocity north, millimeters / second
1094
0
    session->gpsdata.gst.vn_err_deviation = getles32(buf, 4)/ 1000.0;
1095
    // velocity up, millimeters / second
1096
0
    session->gpsdata.gst.vu_err_deviation = getles32(buf, 4)/ 1000.0;
1097
1098
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1099
0
        "ALLY: NAV-PVERR: iTOW %lld stdlat %.3f stdlon %.3f stdalt %.3f "
1100
0
        "stdve %.3f stdvn %.3f stdvu %.3f\n",
1101
0
        (long long)session->driver.ubx.iTOW,
1102
0
        session->gpsdata.gst.lat_err_deviation,
1103
0
        session->gpsdata.gst.lon_err_deviation,
1104
0
        session->gpsdata.gst.alt_err_deviation,
1105
0
        session->gpsdata.gst.ve_err_deviation,
1106
0
        session->gpsdata.gst.vn_err_deviation,
1107
0
        session->gpsdata.gst.vu_err_deviation);
1108
1109
0
    mask = GST_SET | ONLINE_SET;
1110
0
    return mask;
1111
0
}
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
0
{
1121
0
    gps_mask_t mask = 0;
1122
0
    unsigned mode = MODE_NOT_SEEN;
1123
0
    unsigned status = STATUS_UNK;
1124
0
    struct tm unpacked_date = {0};
1125
0
    timespec_t ts_tow;
1126
1127
0
    unsigned year = getleu16(buf, 4);
1128
0
    unsigned month = getub(buf, 6);
1129
0
    unsigned day = getub(buf, 7);
1130
0
    unsigned hour = getub(buf, 8);
1131
0
    unsigned min = getub(buf, 9);
1132
0
    unsigned sec = getub(buf, 10);
1133
0
    unsigned valid = getub(buf, 10);      // undocumented!
1134
0
    unsigned long long tAcc = getleu32(buf, 12);
1135
0
    long long nano = getles32(buf, 16);
1136
0
    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
0
    unsigned numSV = getub(buf, 23);
1141
0
    long long lon = getles32(buf, 24);
1142
0
    long long lat = getles32(buf, 28);
1143
0
    long long altHAE = getles32(buf, 32);
1144
0
    long long hMSL = getles32(buf, 36);
1145
0
    unsigned long long hAcc = getleu32(buf, 40);
1146
0
    unsigned long long vAcc = getleu32(buf, 44);
1147
0
    long long Vel_N = getles32(buf, 48);
1148
0
    long long Vel_E = getles32(buf, 52);
1149
0
    long long Vel_D = getles32(buf, 56);
1150
0
    long long gSpeed = getles32(buf, 60);        // signed??
1151
0
    unsigned long long headMot = getles32(buf, 64);
1152
0
    long long sAcc = getleu32(buf, 68);
1153
0
    long long headAcc = getleu32(buf, 72);
1154
0
    int pDOP = getles16(buf, 76);
1155
    // Different than headMot ??
1156
0
    unsigned long long headVeh = getles32(buf, 84);
1157
1158
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
1159
0
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1160
1161
0
    switch (fixType) {
1162
0
    case 0:    // No fix
1163
0
        mode |= MODE_NO_FIX;
1164
0
        status = STATUS_UNK;
1165
0
        mask |= STATUS_SET | MODE_SET;
1166
0
        break;
1167
0
    case 1:    // Aided fix ??
1168
0
        FALLTHROUGH
1169
0
    case 2:    // Clock bias fix ??
1170
        // assume these are like surveyed-in
1171
0
        mode = MODE_3D;
1172
0
        status = STATUS_TIME;
1173
0
        mask |= STATUS_SET | MODE_SET;
1174
0
        break;
1175
0
    case 3:         // 2D fix
1176
0
        mode = MODE_2D;
1177
0
        status = STATUS_GPS;
1178
0
        mask |= MODE_SET | STATUS_SET | LATLON_SET;
1179
0
        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
0
    default:
1201
        // huh?
1202
0
        break;
1203
0
    }
1204
0
    session->newdata.mode = mode;
1205
0
    session->newdata.status = status;
1206
1207
0
    unpacked_date.tm_year = year;
1208
0
    unpacked_date.tm_mon = month;
1209
0
    unpacked_date.tm_mday = day;
1210
0
    unpacked_date.tm_hour = hour;
1211
0
    unpacked_date.tm_min = min;
1212
0
    unpacked_date.tm_sec = sec;
1213
    // FIXME:  add in fractional iTOW
1214
0
    session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
1215
0
    session->newdata.time.tv_nsec = nano;
1216
0
    TS_NORM(&session->newdata.time);
1217
0
    if (0 < fixType) {
1218
        // accept DR
1219
0
        mask |= TIME_SET | NTPTIME_IS | GOODTIME_IS;
1220
0
    }
1221
1222
0
    session->newdata.longitude = lon / 1e7;
1223
0
    session->newdata.latitude = lat / 1e7;
1224
0
    session->newdata.altHAE = altHAE / 1e3;
1225
0
    session->newdata.altMSL = hMSL / 1e3;
1226
0
    session->newdata.speed = gSpeed / 1e3;
1227
1228
0
    session->newdata.track = headMot / 100.0;
1229
0
    mask |= TRACK_SET;
1230
1231
0
    if (9999 > pDOP) {
1232
0
        session->gpsdata.dop.pdop = pDOP / 100.0;
1233
0
        mask |= DOP_SET;
1234
0
    }
1235
0
    session->newdata.NED.velN = Vel_N / 1000.0;
1236
0
    session->newdata.NED.velE = Vel_E / 1000.0;
1237
0
    session->newdata.NED.velD = Vel_D / 1000.0;
1238
1239
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1240
0
             "ALLY: NAV-PVT: time %lld%.09ld %u/%u/%u %u:%02u:%02u valid x%x "
1241
0
             "tAcc %llu fixType %u  numSv %u lon %.7f lat %.7f "
1242
0
             "altHAE %.3f altMSL %.3f hAcc %llu vAcc %llu "
1243
0
             "speed %.3f, headMot %.2f sAcc ^%.55f headAcc %.5f pDOP %.2f "
1244
0
             "headVeh %.2f\n",
1245
0
             (long long)session->newdata.time.tv_sec,
1246
0
             session->newdata.time.tv_nsec,
1247
0
             year, month, day, hour, min, sec,
1248
0
             valid, tAcc,
1249
0
             fixType, numSV,
1250
0
             session->newdata.longitude,
1251
0
             session->newdata.latitude,
1252
0
             session->newdata.altHAE,
1253
0
             session->newdata.altMSL,
1254
0
             hAcc, vAcc,
1255
0
             session->newdata.speed,
1256
0
             session->newdata.track,
1257
0
             sAcc / 1e5, headAcc / 1e5,
1258
0
             session->gpsdata.dop.pdop,
1259
0
             headVeh / 1e5);
1260
0
     GPSD_LOG(LOG_IO, &session->context->errout,
1261
0
              "ALLY: NAV-PVT fixType %u(%s) mode %u(%s) status %u(%s)\n",
1262
0
             fixType, val2str(fixType, vfixType),
1263
0
             session->newdata.mode,
1264
0
             val2str(session->newdata.mode, vmode_str),
1265
0
             session->newdata.status,
1266
0
             val2str(session->newdata.status, vstatus_str));
1267
0
    return mask;
1268
0
}
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
0
{
1287
    // char buf2[80];
1288
0
    unsigned i, nsv, st;
1289
0
    uint32_t nchan;
1290
0
    timespec_t ts_tow;
1291
1292
0
    session->driver.ubx.iTOW = getleu32(buf, 0);
1293
0
    MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1294
0
    session->gpsdata.skyview_time =
1295
0
        gpsd_gpstime_resolv(session, session->context->gps_week, ts_tow);
1296
1297
0
    nchan = getleu32(buf, 4);  // 32 bits, seriously??
1298
0
    if (nchan > MAXCHANNELS) {
1299
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1300
0
                 "ALLY: NAV SVINFO: runt >%d reported visible\n",
1301
0
                 MAXCHANNELS);
1302
0
        return 0;
1303
0
    }
1304
1305
0
    if ((nchan * 24 + 8) > payload_len) {
1306
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1307
0
                 "ALLY: NAV SVINFO: runt payload %d channels\n",
1308
0
                 nchan);
1309
0
        return 0;
1310
0
    }
1311
1312
0
    gpsd_zero_satellites(&session->gpsdata);
1313
0
    nsv = 0;
1314
0
    for (i = st = 0; i < nchan; i++) {
1315
0
        char buf2[80];
1316
        // No info on sigid???
1317
0
        unsigned off = 24 * i;
1318
0
        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
0
        unsigned ally_svid = getleu16(buf, off + 8);
1322
        // no doc on flags
1323
0
        unsigned flags = getub(buf, off + 10);
1324
        // no doc on quality
1325
0
        unsigned quality = getub(buf, off + 11);
1326
0
        unsigned cno = getub(buf, off + 12);
1327
0
        int el = getsb(buf, off + 13);
1328
0
        int az = getles16(buf, off + 14);
1329
0
        long long prRes = getles32(buf, off + 16);  // pseudorange residue, cm
1330
0
        unsigned ubx_gnssid, ubx_sigid;
1331
1332
0
        unsigned ubx_svid = ally_svid_to_ids(session, ally_svid, &ubx_gnssid,
1333
0
                                             &ubx_sigid);
1334
1335
0
        if (1 > ubx_svid) {
1336
           // skip bad PRN
1337
0
           GPSD_LOG(LOG_WARN, &session->context->errout,
1338
0
                    "ALLY: NAV-SVINFO bad svid %u\n", ally_svid);
1339
0
           continue;
1340
0
        }
1341
0
        nmea_PRN = ally_svid;
1342
        // pr Rate, m/s
1343
0
        session->gpsdata.skyview[st].prRate = getlef32((const char*)buf,
1344
0
                                                        off + 20);
1345
        // pseudorange, m
1346
0
        session->gpsdata.skyview[st].pr = getled64((const char*)buf, off + 24);
1347
1348
0
        session->gpsdata.skyview[st].PRN = nmea_PRN;
1349
0
        session->gpsdata.skyview[st].gnssid = ubx_gnssid;
1350
0
        session->gpsdata.skyview[st].svid = ubx_svid;
1351
1352
0
        session->gpsdata.skyview[st].ss = (double)cno;
1353
0
        if (90 >= abs(el)) {
1354
0
            session->gpsdata.skyview[st].elevation = (double)el;
1355
0
        }
1356
0
        if (359 > az &&
1357
0
            0 <= az) {
1358
0
            session->gpsdata.skyview[st].azimuth = (double)az;
1359
0
        }
1360
0
        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
0
        session->gpsdata.skyview[st].qualityInd = quality & 7;
1365
1366
        // No health data, no used data.
1367
        // flags and quality undocumented.
1368
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1369
0
                 "ALLY: NAV-SVINFO ally_svid %u  gnssid %d(%s) svid %d "
1370
0
                 "sigid %d flags x%x az %.0f el %.0f cno %.0f prRes %.2f "
1371
0
                 "quality %u, prRate %f pr %.4f\n",
1372
0
                 ally_svid,
1373
0
                 session->gpsdata.skyview[st].gnssid,
1374
0
                 val2str(session->gpsdata.skyview[st].gnssid, vgnssId),
1375
0
                 session->gpsdata.skyview[st].svid, ubx_sigid, flags,
1376
0
                 session->gpsdata.skyview[st].azimuth,
1377
0
                 session->gpsdata.skyview[st].elevation,
1378
0
                 session->gpsdata.skyview[st].ss,
1379
0
                 session->gpsdata.skyview[st].prRes,
1380
0
                 session->gpsdata.skyview[st].qualityInd,
1381
0
                 session->gpsdata.skyview[st].prRate,
1382
0
                 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
0
        GPSD_LOG(LOG_IO, &session->context->errout,
1387
0
                 "ALLY: NAV-SVINFO flags %ss\n",
1388
0
                 flags2str(flags, fsvinfo_flags, buf2, sizeof(buf2)));
1389
0
        st++;
1390
0
    }
1391
1392
    // session->gpsdata.satellites_visible = (int)st;
1393
    // session->gpsdata.satellites_used = (int)nsv;
1394
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1395
0
             "ALLY: NAV-SVINFO: visible=%d used=%d\n",
1396
0
             st, nsv);
1397
    // not ready for prime time, no sigid!!
1398
    // return SATELLITE_SET | USED_IS;
1399
0
    return 0;
1400
0
}
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
0
{
1408
0
    unsigned idx;
1409
0
    gps_mask_t mask = 0;
1410
    // char ts_buf[TIMESPEC_LEN];
1411
1412
0
    unsigned numSV = getub(buf, 4);                // 32 bits?!?!
1413
1414
0
    if (payload_len < (8 + (4 * numSV))) {
1415
        // length check
1416
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1417
0
                 "ALLY: NAV-SVSTATE: bad length %zd", payload_len);
1418
0
    }
1419
0
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1420
    // 5 reserved
1421
1422
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1423
0
             "ALLY: NAV-SVSTATE: iTOW %lld numSV %u payload_len %lld\n",
1424
0
             (long long)session->driver.ubx.iTOW, numSV,
1425
0
             (long long) payload_len);
1426
0
    for (idx = 8; idx < payload_len; idx += 4) {
1427
0
        unsigned svid = getleu16(buf, idx);         // NMEA PRN
1428
0
        unsigned eph_state = getub(buf, idx + 2);   // ephemeris state
1429
0
        unsigned alm_state = getub(buf, idx + 3);   // ephemeris state
1430
0
        unsigned ubx_gnssid, ubx_sigid;
1431
1432
0
        unsigned ubx_svid = ally_svid_to_ids(session, svid, &ubx_gnssid,
1433
0
                                             &ubx_sigid);
1434
1435
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1436
0
                 "ALLY: NAV-SVSTATE: svid %4u gnssid %u(%s) usvid %u sigid %u "
1437
0
                 "eph x%x(use %d viz %u(%s) hlth %u(%s) "
1438
0
                 "alm x%x(use %d asrc %u(%s) esrc %u(%s))\n",
1439
0
                 svid, ubx_gnssid, val2str(ubx_gnssid, vgnssId),
1440
0
                 ubx_svid, ubx_sigid,
1441
0
                 eph_state, (eph_state >> 4) & 0x0f,
1442
0
                 (eph_state >> 2) & 0x03,
1443
0
                 val2str((eph_state >> 2) & 0x03, vsvstate_viz),
1444
0
                 eph_state & 0x03,
1445
0
                 val2str(eph_state & 0x03, vsvstate_hlth),
1446
0
                 alm_state,
1447
0
                 (alm_state >> 4) & 0x0f,
1448
0
                 (alm_state >> 2) & 0x03,
1449
0
                 val2str((alm_state >> 2) & 0x03, vsvstate_src),
1450
0
                 alm_state & 0x03,
1451
0
                 val2str(alm_state & 0x03, vsvstate_src));
1452
0
    }
1453
0
    return mask;
1454
0
}
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
0
{
1463
0
    char buf2[80];
1464
0
    gps_mask_t mask = 0;
1465
0
    char ts_buf[TIMESPEC_LEN];
1466
1467
    // TAU1202 returns 21 ???
1468
0
    unsigned navSys = getub(buf, 0);               // which constellation
1469
0
    unsigned flag = getub(buf, 1);                 // Validity flags
1470
0
    int FracTow = getles16(buf, 2);                // fractional TOW, ns
1471
0
    unsigned week = getleu16(buf, 8);
1472
0
    int leapSec = getsb(buf, 10);
1473
    // timeErr in ns, unknown type (1 sigma, 50%, etc.)
1474
0
    double timeErr = (double)getleu32(buf, 12);
1475
1476
0
    session->driver.ubx.iTOW = getleu32(buf, 4);   // refTow, ms
1477
1478
    // Valid leap seconds ?
1479
0
    if (4 == (flag & 4)) {
1480
0
        session->context->leap_seconds = leapSec;
1481
0
        session->context->valid |= LEAP_SECOND_VALID;
1482
0
    }
1483
1484
    // Valid GPS time of week and week number
1485
0
    if (3 == (flag & 3)) {
1486
0
        timespec_t ts_tow;
1487
1488
0
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1489
0
        ts_tow.tv_nsec += FracTow;
1490
0
        TS_NORM(&ts_tow);
1491
0
        session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
1492
1493
0
        session->newdata.ept = timeErr / 1e9;
1494
0
        mask |= (TIME_SET | NTPTIME_IS);
1495
0
    }
1496
1497
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1498
0
             "ALLY: NAV-TIME: time=%s navSys %u flag x%x FracTow %d "
1499
0
             "refTow %lld week %u leapSec %d timeErr %g\n",
1500
0
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1501
0
             navSys, flag, FracTow,
1502
0
             (long long)session->driver.ubx.iTOW, week,
1503
0
             leapSec, timeErr);
1504
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1505
0
             "ALLY: NAV-TIME: navSys %s flag:%s\n",
1506
0
       val2str(navSys, vtime_navsys),
1507
0
       flags2str(flag, vtime_flags, buf2, sizeof(buf2)));
1508
0
    return mask;
1509
0
}
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
0
{
1517
0
    char buf2[80];
1518
0
    gps_mask_t mask = 0;
1519
    // char ts_buf[TIMESPEC_LEN];
1520
1521
    // tAcc in ns, unknown type (1 sigma, 50%, etc.)
1522
0
    unsigned long long tAcc = getleu32(buf, 4);
1523
0
    long long nano = getles32(buf, 8);        // fractional TOW, ns
1524
0
    unsigned year = getleu16(buf, 12);
1525
0
    unsigned month = getub(buf, 14);
1526
0
    unsigned day = getub(buf, 15);
1527
0
    unsigned hour = getub(buf, 16);
1528
0
    unsigned min = getub(buf, 17);
1529
0
    unsigned sec = getub(buf, 18);
1530
0
    unsigned ValidFlag = getub(buf, 19);
1531
0
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1532
1533
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1534
0
             "ALLY: NAV-TIMEUTC: iTOW %lld tAcc %llu nano %lld "
1535
0
             "time %u/%u/%u %u:%02u:%02u ValidFlag x%x\n",
1536
0
             (long long)session->driver.ubx.iTOW, tAcc, nano,
1537
0
             year, month, day, hour, min, sec, ValidFlag);
1538
             // timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1539
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1540
0
             "ALLY: NAV-TIMEUTC: ValidFlag:x%x(%s) utc %s\n",
1541
0
             ValidFlag,
1542
0
             flags2str(ValidFlag, vtimeutc_valid, buf2, sizeof(buf2)),
1543
0
             val2str(ValidFlag, vtimeutc_utc));
1544
0
    return mask;
1545
0
}
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
0
{
1554
0
    gps_mask_t mask = VECEF_SET;
1555
1556
0
    session->driver.ubx.iTOW = getleu32(buf, 0);              // iTow, ms
1557
0
    session->newdata.ecef.vx = getles32(buf, 4) / 100.0;      // cm/s
1558
0
    session->newdata.ecef.vy = getles32(buf, 8) / 100.0;      // cm/s
1559
0
    session->newdata.ecef.vz = getles32(buf, 12) / 100.0;     // cm/s
1560
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1561
0
    session->newdata.ecef.vAcc = getleu32(buf, 16) / 100.0;   // cm/s
1562
1563
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1564
0
             "ALLY: NAV-VELECEF: iTOW %lld vECEF %.2f %.2f %.2f sAcc %.2f\n",
1565
0
             (long long)session->driver.ubx.iTOW,
1566
0
             session->newdata.ecef.vx,
1567
0
             session->newdata.ecef.vy,
1568
0
             session->newdata.ecef.vz,
1569
0
             session->newdata.ecef.vAcc);
1570
0
    return mask;
1571
0
}
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
0
{
1580
0
    gps_mask_t mask = VNED_SET;
1581
1582
0
    session->driver.ubx.iTOW = getleu32(buf, 0);      // iTow, ms
1583
0
    session->newdata.NED.velN = getles32(buf, 4) / 100.0;
1584
0
    session->newdata.NED.velE = getles32(buf, 8) / 100.0;
1585
0
    session->newdata.NED.velD = getles32(buf, 12) / 100.0;
1586
1587
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1588
0
    session->newdata.ecef.vAcc = getleu32(buf, 28) / 100.0;   // cm/s
1589
1590
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1591
0
             "ALLY: NAV-VELNED: iTOW %lld vNED %.2f %.2f %.2f sAcc %.2f\n",
1592
0
             (long long)session->driver.ubx.iTOW,
1593
0
             session->newdata.NED.velN,
1594
0
             session->newdata.NED.velE,
1595
0
             session->newdata.NED.velD,
1596
0
             session->newdata.ecef.vAcc);
1597
0
    return mask;
1598
0
}
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
0
{
1613
0
    unsigned msgid = getbes16(buf, 2);
1614
0
    gps_mask_t mask = 0;
1615
0
    size_t needed_len;
1616
0
    const char *msg_name;
1617
0
    gps_mask_t (* p_decode)(struct gps_device_t *, unsigned char *, size_t);
1618
1619
0
    switch (msgid) {
1620
0
    case ACK_NAK:
1621
0
        needed_len = 2;
1622
0
        msg_name = "ACK-NAK";
1623
0
        p_decode = msg_ack_nak;
1624
0
        break;
1625
0
    case ACK_ACK:
1626
0
        needed_len = 2;
1627
0
        msg_name = "ACK-ACK";
1628
0
        p_decode = msg_ack_ack;
1629
0
        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
0
    case CFG_PRT:
1661
0
        msg_name = "CFG-PRT";
1662
0
        needed_len = 2;
1663
0
        p_decode = msg_cfg_prt;
1664
0
        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
0
    case MON_VER:
1671
0
        msg_name ="MON-VER";
1672
0
        needed_len = 32;
1673
0
        p_decode = msg_mon_ver;
1674
0
        break;
1675
0
    case NAV_AUTO:
1676
0
        msg_name ="NAV-AUTO";
1677
0
        needed_len = 32;
1678
0
        p_decode = msg_nav_auto;
1679
0
        break;
1680
0
    case NAV_CLOCK:
1681
0
        msg_name ="NAV-CLOCK";
1682
0
        needed_len = 20;
1683
0
        p_decode = msg_nav_clock;
1684
0
        break;
1685
0
    case NAV_CLOCK2:
1686
0
        msg_name ="NAV-CLOCK2";
1687
0
        needed_len = 8;
1688
0
        p_decode = msg_nav_clock2;
1689
0
        break;
1690
0
    case NAV_DOP:
1691
0
        msg_name ="NAV-DOP";
1692
0
        needed_len = 18;
1693
0
        p_decode = msg_nav_dop;
1694
0
        break;
1695
0
    case NAV_POSECEF:
1696
0
        msg_name ="NAV-POSECEF";
1697
0
        needed_len = 20;
1698
0
        p_decode = msg_nav_posecef;
1699
0
        break;
1700
0
    case NAV_POSLLH:
1701
0
        msg_name ="NAV-POSLLH";
1702
0
        needed_len = 28;
1703
0
        p_decode = msg_nav_posllh;
1704
0
        break;
1705
0
    case NAV_PVERR:
1706
0
        msg_name ="NAV-PVERR";
1707
0
        needed_len = 28;
1708
0
        p_decode = msg_nav_pverr;
1709
0
        break;
1710
0
    case NAV_PVT:
1711
0
        msg_name ="NAV-PVT";
1712
0
        needed_len = 88;
1713
0
        p_decode = msg_nav_pvt;
1714
0
        break;
1715
0
    case NAV_SVINFO:
1716
0
        msg_name ="NAV-SVINFO";
1717
0
        needed_len = 8;
1718
0
        p_decode = msg_nav_svinfo;
1719
0
        break;
1720
0
    case NAV_SVSTATE:
1721
0
        msg_name ="NAV-SVSTATE";
1722
0
        needed_len = 8;
1723
0
        p_decode = msg_nav_svstate;
1724
0
        break;
1725
0
    case NAV_TIME:
1726
0
        msg_name ="NAV-TIME";
1727
0
        needed_len = 16;
1728
0
        p_decode = msg_nav_time;
1729
0
        break;
1730
0
    case NAV_TIMEUTC:
1731
0
        msg_name ="NAV-TIMEUTC";
1732
0
        needed_len = 20;
1733
0
        p_decode = msg_nav_timeutc;
1734
0
        break;
1735
0
    case NAV_VELECEF:
1736
0
        msg_name ="NAV-VELECEF";
1737
0
        needed_len = 20;
1738
0
        p_decode = msg_nav_velecef;
1739
0
        break;
1740
0
    case NAV_VELNED:
1741
0
        msg_name ="NAV-VELNED";
1742
0
        needed_len = 36;
1743
0
        p_decode = msg_nav_velned;
1744
0
        break;
1745
0
    default:
1746
0
        msg_name ="NAV-unk";
1747
0
        needed_len = 0;
1748
0
        p_decode = NULL;
1749
0
        break;
1750
0
    }
1751
0
    if (needed_len > payload_len) {
1752
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1753
0
                 "ALLY: %s: runt payload len %zd need %zd\n",
1754
0
                 msg_name, payload_len, needed_len);
1755
0
        return 0;
1756
0
    }
1757
0
    if (NULL == p_decode) {
1758
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1759
0
                 "ALLY: Unknown %02x)%s)-%02x payload_len %zd\n",
1760
0
                 (msgid >> 8) & 0xff,
1761
0
                 val2str((msgid >> 8) & 0xff, vclass),
1762
0
                 msgid & 0xff, payload_len);
1763
0
        return 0;
1764
0
    }
1765
0
    mask = p_decode(session, &buf[ALLY_PREFIX_LEN], payload_len);
1766
0
    return mask;
1767
0
}
1768
1769
static gps_mask_t ally_parse(struct gps_device_t * session, unsigned char *buf,
1770
                            size_t len)
1771
0
{
1772
0
    size_t payload_len;
1773
0
    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
0
    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
0
    session->driver.ubx.iTOW = -1;        // set by decoder
1787
1788
    // extract payload length, check against actual length
1789
0
    payload_len = getles16(buf, 4);
1790
1791
0
    if ((len - 8) != payload_len) {
1792
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1793
0
                 "ALLY: len (%zu) does not match payload (%zu) + 8\n",
1794
0
                 len, payload_len);
1795
0
        return 0;
1796
0
    }
1797
1798
0
    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
0
    if (!session->context->readonly &&
1804
0
        0 <= session->queue &&
1805
0
        100 > session->queue) {
1806
0
        unsigned char msg[4] = {0};
1807
1808
0
        GPSD_LOG(LOG_DATA, &session->context->errout,
1809
0
                 "ALLY: queue %d\n", session->queue);
1810
1811
1812
0
        switch (session->queue) {
1813
0
        case 10:
1814
            // start at 10 so the initial configuration can finish first.
1815
            // Poll CFG-ANTIJAM
1816
0
            (void)ally_write(session, ALLY_CFG, 0x15, NULL, 0);
1817
0
            break;
1818
0
        case 14:
1819
            // Poll CFG_CARRSMOOTH
1820
0
            (void)ally_write(session, ALLY_CFG, 0x17, NULL, 0);
1821
0
            break;
1822
0
        case 18:
1823
            // Poll CFG_GEOFENCE
1824
0
            (void)ally_write(session, ALLY_CFG, 0x18, NULL, 0);
1825
0
            break;
1826
0
        case 22:
1827
            // Poll CFG-NAVSAT
1828
0
            (void)ally_write(session, ALLY_CFG, 0x0c, NULL, 0);
1829
0
            break;
1830
0
        case 26:
1831
            // Poll CFG-NMEAVER
1832
0
            (void)ally_write(session, ALLY_CFG, 0x43, NULL, 0);
1833
0
            break;
1834
0
        case 30:
1835
            // Poll CFG-PRT
1836
0
            (void)ally_write(session, ALLY_CFG, 0x00, NULL, 0);
1837
0
            break;
1838
0
        case 34:
1839
            // Poll CFG-PRT(UART1)
1840
0
            msg[0] = 0;
1841
0
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1842
0
            break;
1843
0
        case 38:
1844
            // Poll CFG-PRT(UART2)
1845
0
            msg[0] = 1;
1846
0
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1847
0
            break;
1848
0
        case 40:
1849
            // Poll CFG-SBAS
1850
0
            (void)ally_write(session, ALLY_CFG, 0x0e, NULL, 0);
1851
0
            break;
1852
0
        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
0
            break;
1859
0
        default:
1860
0
            break;
1861
0
        }
1862
0
        session->queue++;
1863
0
    }
1864
1865
0
    return mask;
1866
0
}
1867
1868
static gps_mask_t parse_input(struct gps_device_t *session)
1869
0
{
1870
0
    if (ALLYSTAR_PACKET == session->lexer.type) {
1871
0
        return ally_parse(session, session->lexer.outbuffer,
1872
0
                          session->lexer.outbuflen);
1873
0
    }
1874
    // a comment, JSON, or NMEA 0183
1875
0
    return generic_parse_input(session);
1876
0
}
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
0
{
1890
0
    unsigned char msg[4] = {0};
1891
0
    unsigned u;
1892
1893
    // TAU1202 seems OK with being blasted with CFG-MSG's.
1894
0
    static ally_msgs_t all_nav[] = {
1895
        // prolly no need for NAV-AUTO and NAV-POLL
1896
0
        NAV_AUTO,
1897
0
        NAV_CLOCK,
1898
        // NAV_CLOCK2,           // unsupported TAU1202
1899
0
        NAV_DOP,
1900
0
        NAV_POSECEF,
1901
0
        NAV_POSLLH,
1902
        // NAV_PVERR,            // unsupported TAU1202
1903
        // NAV_PVT,              // unsupported TAU1202
1904
0
        NAV_SVINFO,
1905
0
        NAV_SVSTATE,
1906
0
        NAV_TIME,
1907
0
        NAV_TIMEUTC,
1908
        // NAV_VELECEF,          // unsupported TAU1202
1909
        // NAV_VELNED,           // unsupported TAU1202
1910
0
    };
1911
1912
    // turn on all binary NAV- messages we know of
1913
0
    for (u = 0; u < ROWS(all_nav); u++) {
1914
        // turn on rate one
1915
0
        putbe16(msg, 0, all_nav[u]);
1916
0
        msg[2] = 0x01;          // rate, one
1917
0
        (void)ally_write(session, ALLY_CFG, 0x01, msg, 3);
1918
0
    }
1919
0
}
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
0
{
1959
0
    if (session->context->readonly) {
1960
0
        return;
1961
0
    }
1962
0
    if (event == EVENT_IDENTIFIED) {
1963
0
        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
0
        if (session->context->passive) {
1968
            /* passive mode, do no autoconfig
1969
             * but we really want MON-VER. */
1970
0
            (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1971
0
        } else if (O_OPTIMIZE == session->mode) {
1972
0
            ally_mode(session, MODE_BINARY);
1973
            // restart init queue
1974
0
            session->queue = 0;
1975
0
        } else {
1976
            //* Turn off NMEA output, turn on UBX on this port.
1977
0
            ally_mode(session, MODE_NMEA);
1978
0
        }
1979
0
    } 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
0
}
1987
1988
static void init_query(struct gps_device_t *session)
1989
0
{
1990
    // MON-VER: query for version information
1991
    (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1992
0
}
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