Coverage Report

Created: 2025-10-12 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.26.2~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 UNUSED)
1286
0
{
1287
    // char buf2[80];
1288
0
    unsigned i, nsv, st;
1289
0
    long long 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
    // FIXME: check payload_len
1306
0
    gpsd_zero_satellites(&session->gpsdata);
1307
0
    nsv = 0;
1308
0
    for (i = st = 0; i < nchan; i++) {
1309
0
        char buf2[80];
1310
        // No info on sigid???
1311
0
        unsigned off = 24 * i;
1312
0
        unsigned nmea_PRN;
1313
        // v 2.3.1, 8 bit chand and 8-bit svid became 16-bit svid.
1314
        // no doc on svid numbers... same as NMEA??
1315
0
        unsigned ally_svid = getleu16(buf, off + 8);
1316
        // no doc on flags
1317
0
        unsigned flags = getub(buf, off + 10);
1318
        // no doc on quality
1319
0
        unsigned quality = getub(buf, off + 11);
1320
0
        unsigned cno = getub(buf, off + 12);
1321
0
        int el = getsb(buf, off + 13);
1322
0
        int az = getles16(buf, off + 14);
1323
0
        long long prRes = getles32(buf, off + 16);  // pseudorange residue, cm
1324
0
        unsigned ubx_gnssid, ubx_sigid;
1325
1326
0
        unsigned ubx_svid = ally_svid_to_ids(session, ally_svid, &ubx_gnssid,
1327
0
                                             &ubx_sigid);
1328
1329
0
        if (1 > ubx_svid) {
1330
           // skip bad PRN
1331
0
           GPSD_LOG(LOG_WARN, &session->context->errout,
1332
0
                    "ALLY: NAV-SVINFO bad svid %u\n", ally_svid);
1333
0
           continue;
1334
0
        }
1335
0
        nmea_PRN = ally_svid;
1336
        // pr Rate, m/s
1337
0
        session->gpsdata.skyview[st].prRate = getlef32((const char*)buf,
1338
0
                                                        off + 20);
1339
        // pseudorange, m
1340
0
        session->gpsdata.skyview[st].pr = getled64((const char*)buf, off + 24);
1341
1342
0
        session->gpsdata.skyview[st].PRN = nmea_PRN;
1343
0
        session->gpsdata.skyview[st].gnssid = ubx_gnssid;
1344
0
        session->gpsdata.skyview[st].svid = ubx_svid;
1345
1346
0
        session->gpsdata.skyview[st].ss = (double)cno;
1347
0
        if (90 >= abs(el)) {
1348
0
            session->gpsdata.skyview[st].elevation = (double)el;
1349
0
        }
1350
0
        if (359 > az &&
1351
0
            0 <= az) {
1352
0
            session->gpsdata.skyview[st].azimuth = (double)az;
1353
0
        }
1354
0
        session->gpsdata.skyview[st].prRes = prRes / 100.0;
1355
1356
        /* Undocumented, but (quality & 7) seems to be same as
1357
         * the u-blox qualityInd */
1358
0
        session->gpsdata.skyview[st].qualityInd = quality & 7;
1359
1360
        // No health data, no used data.
1361
        // flags and quality undocumented.
1362
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1363
0
                 "ALLY: NAV-SVINFO ally_svid %u  gnssid %d(%s) svid %d "
1364
0
                 "sigid %d flags x%x az %.0f el %.0f cno %.0f prRes %.2f "
1365
0
                 "quality %u, prRate %f pr %.4f\n",
1366
0
                 ally_svid,
1367
0
                 session->gpsdata.skyview[st].gnssid,
1368
0
                 val2str(session->gpsdata.skyview[st].gnssid, vgnssId),
1369
0
                 session->gpsdata.skyview[st].svid, ubx_sigid, flags,
1370
0
                 session->gpsdata.skyview[st].azimuth,
1371
0
                 session->gpsdata.skyview[st].elevation,
1372
0
                 session->gpsdata.skyview[st].ss,
1373
0
                 session->gpsdata.skyview[st].prRes,
1374
0
                 session->gpsdata.skyview[st].qualityInd,
1375
0
                 session->gpsdata.skyview[st].prRate,
1376
0
                 session->gpsdata.skyview[st].pr);
1377
        /* flags undocumented, here we assume same as UBX-NAV-SVINFO
1378
         * only  diffCorr,orbitAvail,orbitEphs seem to be used.
1379
         * how do we know if a dat is used in the fix?? */
1380
0
        GPSD_LOG(LOG_IO, &session->context->errout,
1381
0
                 "ALLY: NAV-SVINFO flags %ss\n",
1382
0
                 flags2str(flags, fsvinfo_flags, buf2, sizeof(buf2)));
1383
0
        st++;
1384
0
    }
1385
1386
    // session->gpsdata.satellites_visible = (int)st;
1387
    // session->gpsdata.satellites_used = (int)nsv;
1388
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1389
0
             "ALLY: NAV-SVINFO: visible=%d used=%d\n",
1390
0
             st, nsv);
1391
    // not ready for prime time, no sigid!!
1392
    // return SATELLITE_SET | USED_IS;
1393
0
    return 0;
1394
0
}
1395
1396
/**
1397
 * NAV-SVSTATE
1398
 */
1399
static gps_mask_t msg_nav_svstate(struct gps_device_t *session,
1400
                                  unsigned char *buf, size_t payload_len)
1401
0
{
1402
0
    unsigned idx;
1403
0
    gps_mask_t mask = 0;
1404
    // char ts_buf[TIMESPEC_LEN];
1405
1406
0
    unsigned numSV = getub(buf, 4);                // 32 bits?!?!
1407
1408
0
    if (payload_len < (8 + (4 * numSV))) {
1409
        // length check
1410
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1411
0
                 "ALLY: NAV-SVSTATE: bad length %zd", payload_len);
1412
0
    }
1413
0
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1414
    // 5 reserved
1415
1416
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1417
0
             "ALLY: NAV-SVSTATE: iTOW %lld numSV %u payload_len %lld\n",
1418
0
             (long long)session->driver.ubx.iTOW, numSV,
1419
0
             (long long) payload_len);
1420
0
    for (idx = 8; idx < payload_len; idx += 4) {
1421
0
        unsigned svid = getleu16(buf, idx);         // NMEA PRN
1422
0
        unsigned eph_state = getub(buf, idx + 2);   // ephemeris state
1423
0
        unsigned alm_state = getub(buf, idx + 3);   // ephemeris state
1424
0
        unsigned ubx_gnssid, ubx_sigid;
1425
1426
0
        unsigned ubx_svid = ally_svid_to_ids(session, svid, &ubx_gnssid,
1427
0
                                             &ubx_sigid);
1428
1429
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
1430
0
                 "ALLY: NAV-SVSTATE: svid %4u gnssid %u(%s) usvid %u sigid %u "
1431
0
                 "eph x%x(use %d viz %u(%s) hlth %u(%s) "
1432
0
                 "alm x%x(use %d asrc %u(%s) esrc %u(%s))\n",
1433
0
                 svid, ubx_gnssid, val2str(ubx_gnssid, vgnssId),
1434
0
                 ubx_svid, ubx_sigid,
1435
0
                 eph_state, (eph_state >> 4) & 0x0f,
1436
0
                 (eph_state >> 2) & 0x03,
1437
0
                 val2str((eph_state >> 2) & 0x03, vsvstate_viz),
1438
0
                 eph_state & 0x03,
1439
0
                 val2str(eph_state & 0x03, vsvstate_hlth),
1440
0
                 alm_state,
1441
0
                 (alm_state >> 4) & 0x0f,
1442
0
                 (alm_state >> 2) & 0x03,
1443
0
                 val2str((alm_state >> 2) & 0x03, vsvstate_src),
1444
0
                 alm_state & 0x03,
1445
0
                 val2str(alm_state & 0x03, vsvstate_src));
1446
0
    }
1447
0
    return mask;
1448
0
}
1449
1450
/**
1451
 * GPS Leap Seconds - NAV-TIME
1452
 * sorta like UBX-NAV-TIMEGPS
1453
 */
1454
static gps_mask_t msg_nav_time(struct gps_device_t *session,
1455
                               unsigned char *buf, size_t payload_len UNUSED)
1456
0
{
1457
0
    char buf2[80];
1458
0
    gps_mask_t mask = 0;
1459
0
    char ts_buf[TIMESPEC_LEN];
1460
1461
    // TAU1202 returns 21 ???
1462
0
    unsigned navSys = getub(buf, 0);               // which constellation
1463
0
    unsigned flag = getub(buf, 1);                 // Validity flags
1464
0
    int FracTow = getles16(buf, 2);                // fractional TOW, ns
1465
0
    unsigned week = getleu16(buf, 8);
1466
0
    int leapSec = getsb(buf, 10);
1467
    // timeErr in ns, unknown type (1 sigma, 50%, etc.)
1468
0
    double timeErr = (double)getleu32(buf, 12);
1469
1470
0
    session->driver.ubx.iTOW = getleu32(buf, 4);   // refTow, ms
1471
1472
    // Valid leap seconds ?
1473
0
    if (4 == (flag & 4)) {
1474
0
        session->context->leap_seconds = leapSec;
1475
0
        session->context->valid |= LEAP_SECOND_VALID;
1476
0
    }
1477
1478
    // Valid GPS time of week and week number
1479
0
    if (3 == (flag & 3)) {
1480
0
        timespec_t ts_tow;
1481
1482
0
        MSTOTS(&ts_tow, session->driver.ubx.iTOW);
1483
0
        ts_tow.tv_nsec += FracTow;
1484
0
        TS_NORM(&ts_tow);
1485
0
        session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
1486
1487
0
        session->newdata.ept = timeErr / 1e9;
1488
0
        mask |= (TIME_SET | NTPTIME_IS);
1489
0
    }
1490
1491
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1492
0
             "ALLY: NAV-TIME: time=%s navSys %u flag x%x FracTow %d "
1493
0
             "refTow %lld week %u leapSec %d timeErr %g\n",
1494
0
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1495
0
             navSys, flag, FracTow,
1496
0
             (long long)session->driver.ubx.iTOW, week,
1497
0
             leapSec, timeErr);
1498
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1499
0
             "ALLY: NAV-TIME: navSys %s flag:%s\n",
1500
0
       val2str(navSys, vtime_navsys),
1501
0
       flags2str(flag, vtime_flags, buf2, sizeof(buf2)));
1502
0
    return mask;
1503
0
}
1504
1505
/**
1506
 * NAV-TIMEUTC
1507
 */
1508
static gps_mask_t msg_nav_timeutc(struct gps_device_t *session,
1509
                                  unsigned char *buf, size_t payload_len UNUSED)
1510
0
{
1511
0
    char buf2[80];
1512
0
    gps_mask_t mask = 0;
1513
    // char ts_buf[TIMESPEC_LEN];
1514
1515
    // tAcc in ns, unknown type (1 sigma, 50%, etc.)
1516
0
    unsigned long long tAcc = getleu32(buf, 4);
1517
0
    long long nano = getles32(buf, 8);        // fractional TOW, ns
1518
0
    unsigned year = getleu16(buf, 12);
1519
0
    unsigned month = getub(buf, 14);
1520
0
    unsigned day = getub(buf, 15);
1521
0
    unsigned hour = getub(buf, 16);
1522
0
    unsigned min = getub(buf, 17);
1523
0
    unsigned sec = getub(buf, 18);
1524
0
    unsigned ValidFlag = getub(buf, 19);
1525
0
    session->driver.ubx.iTOW = getleu32(buf, 0);   // iTow, ms
1526
1527
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1528
0
             "ALLY: NAV-TIMEUTC: iTOW %lld tAcc %llu nano %lld "
1529
0
             "time %u/%u/%u %u:%02u:%02u ValidFlag x%x\n",
1530
0
             (long long)session->driver.ubx.iTOW, tAcc, nano,
1531
0
             year, month, day, hour, min, sec, ValidFlag);
1532
             // timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1533
0
    GPSD_LOG(LOG_IO, &session->context->errout,
1534
0
             "ALLY: NAV-TIMEUTC: ValidFlag:x%x(%s) utc %s\n",
1535
0
             ValidFlag,
1536
0
             flags2str(ValidFlag, vtimeutc_valid, buf2, sizeof(buf2)),
1537
0
             val2str(ValidFlag, vtimeutc_utc));
1538
0
    return mask;
1539
0
}
1540
1541
/**
1542
 * NAV-VELECEF
1543
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1544
 */
1545
static gps_mask_t msg_nav_velecef(struct gps_device_t *session,
1546
                                  unsigned char *buf, size_t payload_len UNUSED)
1547
0
{
1548
0
    gps_mask_t mask = VECEF_SET;
1549
1550
0
    session->driver.ubx.iTOW = getleu32(buf, 0);              // iTow, ms
1551
0
    session->newdata.ecef.vx = getles32(buf, 4) / 100.0;      // cm/s
1552
0
    session->newdata.ecef.vy = getles32(buf, 8) / 100.0;      // cm/s
1553
0
    session->newdata.ecef.vz = getles32(buf, 12) / 100.0;     // cm/s
1554
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1555
0
    session->newdata.ecef.vAcc = getleu32(buf, 16) / 100.0;   // cm/s
1556
1557
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1558
0
             "ALLY: NAV-VELECEF: iTOW %lld vECEF %.2f %.2f %.2f sAcc %.2f\n",
1559
0
             (long long)session->driver.ubx.iTOW,
1560
0
             session->newdata.ecef.vx,
1561
0
             session->newdata.ecef.vy,
1562
0
             session->newdata.ecef.vz,
1563
0
             session->newdata.ecef.vAcc);
1564
0
    return mask;
1565
0
}
1566
1567
/**
1568
 * NAV-VELNED
1569
 * Untested, unsupported on TAU1202, SW 3.018.a3f23db
1570
 */
1571
static gps_mask_t msg_nav_velned(struct gps_device_t *session,
1572
                                 unsigned char *buf, size_t payload_len UNUSED)
1573
0
{
1574
0
    gps_mask_t mask = VNED_SET;
1575
1576
0
    session->driver.ubx.iTOW = getleu32(buf, 0);      // iTow, ms
1577
0
    session->newdata.NED.velN = getles32(buf, 4) / 100.0;
1578
0
    session->newdata.NED.velE = getles32(buf, 8) / 100.0;
1579
0
    session->newdata.NED.velD = getles32(buf, 12) / 100.0;
1580
1581
    // sAcc (vAcc) in ns, unknown type (1 sigma, 50%, etc.)
1582
0
    session->newdata.ecef.vAcc = getleu32(buf, 28) / 100.0;   // cm/s
1583
1584
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1585
0
             "ALLY: NAV-VELNED: iTOW %lld vNED %.2f %.2f %.2f sAcc %.2f\n",
1586
0
             (long long)session->driver.ubx.iTOW,
1587
0
             session->newdata.NED.velN,
1588
0
             session->newdata.NED.velE,
1589
0
             session->newdata.NED.velD,
1590
0
             session->newdata.ecef.vAcc);
1591
0
    return mask;
1592
0
}
1593
1594
/* RXM-*
1595
 * undocumented
1596
 * Class RXM, ID 0x01, payload 1, enables RXM-x57.
1597
 * Class RXM, ID 0x01, payload 0, disable RXM-x57.
1598
 *
1599
 * RXM-x57 undocumented.
1600
 */
1601
1602
/* msg_decode() -- dispatch all message types to proper decoder
1603
 */
1604
static gps_mask_t msg_decode(struct gps_device_t *session,
1605
                            unsigned char *buf, size_t payload_len)
1606
0
{
1607
0
    unsigned msgid = getbes16(buf, 2);
1608
0
    gps_mask_t mask = 0;
1609
0
    size_t needed_len;
1610
0
    const char *msg_name;
1611
0
    gps_mask_t (* p_decode)(struct gps_device_t *, unsigned char *, size_t);
1612
1613
0
    switch (msgid) {
1614
0
    case ACK_NAK:
1615
0
        needed_len = 2;
1616
0
        msg_name = "ACK-NAK";
1617
0
        p_decode = msg_ack_nak;
1618
0
        break;
1619
0
    case ACK_ACK:
1620
0
        needed_len = 2;
1621
0
        msg_name = "ACK-ACK";
1622
0
        p_decode = msg_ack_ack;
1623
0
        break;
1624
0
    case CFG_ANTIJAM:
1625
0
        needed_len = 3;
1626
0
        msg_name = "CFG-ANTIJAM";
1627
0
        p_decode = msg_cfg_antijam;
1628
0
        break;
1629
0
    case CFG_CARRSMOOTH:
1630
0
        msg_name = "CFG-CARRSMOOTH";
1631
0
        needed_len = 1;
1632
0
        p_decode = msg_cfg_carrsmooth;
1633
0
        break;
1634
0
    case CFG_GEOFENCE:
1635
0
        msg_name = "CFG-GEOFENCE";
1636
0
        needed_len = 8;
1637
0
        p_decode = msg_cfg_geofence;
1638
0
        break;
1639
0
    case CFG_NAVSAT:
1640
0
        needed_len = 4;
1641
0
        msg_name = "CFG-NAVSAT";
1642
0
        p_decode = msg_cfg_navsat;
1643
0
        break;
1644
0
    case CFG_NMEAVER:
1645
0
        msg_name = "CFG-NMEAVER";
1646
0
        needed_len = 1;
1647
0
        p_decode = msg_cfg_nmeaver;
1648
0
        break;
1649
0
    case CFG_PPS:
1650
0
        msg_name = "CFG-PPS";
1651
0
        needed_len = 5;
1652
0
        p_decode = msg_cfg_pps;
1653
0
        break;
1654
0
    case CFG_PRT:
1655
0
        msg_name = "CFG-PRT";
1656
0
        needed_len = 2;
1657
0
        p_decode = msg_cfg_prt;
1658
0
        break;
1659
0
    case CFG_SBAS:
1660
0
        msg_name = "CFG-SBAS";
1661
0
        needed_len = 0;
1662
0
        p_decode = msg_cfg_sbas;
1663
0
        break;
1664
0
    case MON_VER:
1665
0
        msg_name ="MON-VER";
1666
0
        needed_len = 32;
1667
0
        p_decode = msg_mon_ver;
1668
0
        break;
1669
0
    case NAV_AUTO:
1670
0
        msg_name ="NAV-AUTO";
1671
0
        needed_len = 32;
1672
0
        p_decode = msg_nav_auto;
1673
0
        break;
1674
0
    case NAV_CLOCK:
1675
0
        msg_name ="NAV-CLOCK";
1676
0
        needed_len = 20;
1677
0
        p_decode = msg_nav_clock;
1678
0
        break;
1679
0
    case NAV_CLOCK2:
1680
0
        msg_name ="NAV-CLOCK2";
1681
0
        needed_len = 8;
1682
0
        p_decode = msg_nav_clock2;
1683
0
        break;
1684
0
    case NAV_DOP:
1685
0
        msg_name ="NAV-DOP";
1686
0
        needed_len = 18;
1687
0
        p_decode = msg_nav_dop;
1688
0
        break;
1689
0
    case NAV_POSECEF:
1690
0
        msg_name ="NAV-POSECEF";
1691
0
        needed_len = 20;
1692
0
        p_decode = msg_nav_posecef;
1693
0
        break;
1694
0
    case NAV_POSLLH:
1695
0
        msg_name ="NAV-POSLLH";
1696
0
        needed_len = 28;
1697
0
        p_decode = msg_nav_posllh;
1698
0
        break;
1699
0
    case NAV_PVERR:
1700
0
        msg_name ="NAV-PVERR";
1701
0
        needed_len = 28;
1702
0
        p_decode = msg_nav_pverr;
1703
0
        break;
1704
0
    case NAV_PVT:
1705
0
        msg_name ="NAV-PVT";
1706
0
        needed_len = 88;
1707
0
        p_decode = msg_nav_pvt;
1708
0
        break;
1709
0
    case NAV_SVINFO:
1710
0
        msg_name ="NAV-SVINFO";
1711
0
        needed_len = 8;
1712
0
        p_decode = msg_nav_svinfo;
1713
0
        break;
1714
0
    case NAV_SVSTATE:
1715
0
        msg_name ="NAV-SVSTATE";
1716
0
        needed_len = 8;
1717
0
        p_decode = msg_nav_svstate;
1718
0
        break;
1719
0
    case NAV_TIME:
1720
0
        msg_name ="NAV-TIME";
1721
0
        needed_len = 16;
1722
0
        p_decode = msg_nav_time;
1723
0
        break;
1724
0
    case NAV_TIMEUTC:
1725
0
        msg_name ="NAV-TIMEUTC";
1726
0
        needed_len = 20;
1727
0
        p_decode = msg_nav_timeutc;
1728
0
        break;
1729
0
    case NAV_VELECEF:
1730
0
        msg_name ="NAV-VELECEF";
1731
0
        needed_len = 20;
1732
0
        p_decode = msg_nav_velecef;
1733
0
        break;
1734
0
    case NAV_VELNED:
1735
0
        msg_name ="NAV-VELNED";
1736
0
        needed_len = 36;
1737
0
        p_decode = msg_nav_velned;
1738
0
        break;
1739
0
    default:
1740
0
        msg_name ="NAV-unk";
1741
0
        needed_len = 0;
1742
0
        p_decode = NULL;
1743
0
        break;
1744
0
    }
1745
0
    if (needed_len > payload_len) {
1746
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1747
0
                 "ALLY: %s: runt payload len %zd need %zd\n",
1748
0
                 msg_name, payload_len, needed_len);
1749
0
        return 0;
1750
0
    }
1751
0
    if (NULL == p_decode) {
1752
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1753
0
                 "ALLY: Unknown %02x)%s)-%02x payload_len %zd\n",
1754
0
                 (msgid >> 8) & 0xff,
1755
0
                 val2str((msgid >> 8) & 0xff, vclass),
1756
0
                 msgid & 0xff, payload_len);
1757
0
        return 0;
1758
0
    }
1759
0
    mask = p_decode(session, &buf[ALLY_PREFIX_LEN], payload_len);
1760
0
    return mask;
1761
0
}
1762
1763
static gps_mask_t ally_parse(struct gps_device_t * session, unsigned char *buf,
1764
                            size_t len)
1765
0
{
1766
0
    size_t payload_len;
1767
0
    gps_mask_t mask = 0;
1768
1769
    /* the packet at least 8 bytes / Min packet is 8 ==  header (2),
1770
    *  Message ID (2), length (2) and checksum (2).  The packetizer should
1771
    *  already guarantee, This is to protect against malicious fuzzing. */
1772
0
    if (8 > len) {
1773
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1774
0
                 "ALLY: runt message len %zu\n", len);
1775
0
        return 0;
1776
0
    }
1777
1778
    // session->cycle_end_reliable = true; // Not yet.
1779
    // for now, use driver.ubx.
1780
0
    session->driver.ubx.iTOW = -1;        // set by decoder
1781
1782
    // extract payload length, check against actual length
1783
0
    payload_len = getles16(buf, 4);
1784
1785
0
    if ((len - 8) != payload_len) {
1786
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1787
0
                 "ALLY: len (%zu) does not match payload (%zu) + 8\n",
1788
0
                 len, payload_len);
1789
0
        return 0;
1790
0
    }
1791
1792
0
    mask = msg_decode(session, buf, payload_len);
1793
1794
    /* handle the init queue.  Some parts get cranky when they
1795
     * get too many configuration changes at once.
1796
     */
1797
0
    if (!session->context->readonly &&
1798
0
        0 <= session->queue &&
1799
0
        100 > session->queue) {
1800
0
        unsigned char msg[4] = {0};
1801
1802
0
        GPSD_LOG(LOG_DATA, &session->context->errout,
1803
0
                 "ALLY: queue %d\n", session->queue);
1804
1805
1806
0
        switch (session->queue) {
1807
0
        case 10:
1808
            // start at 10 so the initial configuration can finish first.
1809
            // Poll CFG-ANTIJAM
1810
0
            (void)ally_write(session, ALLY_CFG, 0x15, NULL, 0);
1811
0
            break;
1812
0
        case 14:
1813
            // Poll CFG_CARRSMOOTH
1814
0
            (void)ally_write(session, ALLY_CFG, 0x17, NULL, 0);
1815
0
            break;
1816
0
        case 18:
1817
            // Poll CFG_GEOFENCE
1818
0
            (void)ally_write(session, ALLY_CFG, 0x18, NULL, 0);
1819
0
            break;
1820
0
        case 22:
1821
            // Poll CFG-NAVSAT
1822
0
            (void)ally_write(session, ALLY_CFG, 0x0c, NULL, 0);
1823
0
            break;
1824
0
        case 26:
1825
            // Poll CFG-NMEAVER
1826
0
            (void)ally_write(session, ALLY_CFG, 0x43, NULL, 0);
1827
0
            break;
1828
0
        case 30:
1829
            // Poll CFG-PRT
1830
0
            (void)ally_write(session, ALLY_CFG, 0x00, NULL, 0);
1831
0
            break;
1832
0
        case 34:
1833
            // Poll CFG-PRT(UART1)
1834
0
            msg[0] = 0;
1835
0
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1836
0
            break;
1837
0
        case 38:
1838
            // Poll CFG-PRT(UART2)
1839
0
            msg[0] = 1;
1840
0
            (void)ally_write(session, ALLY_CFG, 0x00, msg, 1);
1841
0
            break;
1842
0
        case 40:
1843
            // Poll CFG-SBAS
1844
0
            (void)ally_write(session, ALLY_CFG, 0x0e, NULL, 0);
1845
0
            break;
1846
0
        case 44:
1847
#if 0       // Debug code
1848
            // turn off RXM-0x01
1849
            msg[0] = 0x00;          // enable?
1850
            (void)ally_write(session, ALLY_RXM, 0x01, msg, 1);
1851
#endif
1852
0
            break;
1853
0
        default:
1854
0
            break;
1855
0
        }
1856
0
        session->queue++;
1857
0
    }
1858
1859
0
    return mask;
1860
0
}
1861
1862
static gps_mask_t parse_input(struct gps_device_t *session)
1863
0
{
1864
0
    if (ALLYSTAR_PACKET == session->lexer.type) {
1865
0
        return ally_parse(session, session->lexer.outbuffer,
1866
0
                          session->lexer.outbuflen);
1867
0
    }
1868
    // a comment, JSON, or NMEA 0183
1869
0
    return generic_parse_input(session);
1870
0
}
1871
1872
// not used by gpsd, it's for gpsctl and friends
1873
static ssize_t control_send(struct gps_device_t *session, char *msg,
1874
                            size_t data_len)
1875
0
{
1876
0
    return ally_write(session, (unsigned int)msg[0], (unsigned int)msg[1],
1877
0
                      (unsigned char *)msg + 2,
1878
0
                      (size_t)(data_len - 2)) ? ((ssize_t) (data_len + 7)) : -1;
1879
0
}
1880
1881
// ally_mode(), stub for NMEA/BINARY changer.
1882
static void ally_mode(struct gps_device_t *session, int mode UNUSED)
1883
0
{
1884
0
    unsigned char msg[4] = {0};
1885
0
    unsigned u;
1886
1887
    // TAU1202 seems OK with being blasted with CFG-MSG's.
1888
0
    static ally_msgs_t all_nav[] = {
1889
        // prolly no need for NAV-AUTO and NAV-POLL
1890
0
        NAV_AUTO,
1891
0
        NAV_CLOCK,
1892
        // NAV_CLOCK2,           // unsupported TAU1202
1893
0
        NAV_DOP,
1894
0
        NAV_POSECEF,
1895
0
        NAV_POSLLH,
1896
        // NAV_PVERR,            // unsupported TAU1202
1897
        // NAV_PVT,              // unsupported TAU1202
1898
0
        NAV_SVINFO,
1899
0
        NAV_SVSTATE,
1900
0
        NAV_TIME,
1901
0
        NAV_TIMEUTC,
1902
        // NAV_VELECEF,          // unsupported TAU1202
1903
        // NAV_VELNED,           // unsupported TAU1202
1904
0
    };
1905
1906
    // turn on all binary NAV- messages we know of
1907
0
    for (u = 0; u < ROWS(all_nav); u++) {
1908
        // turn on rate one
1909
0
        putbe16(msg, 0, all_nav[u]);
1910
0
        msg[2] = 0x01;          // rate, one
1911
0
        (void)ally_write(session, ALLY_CFG, 0x01, msg, 3);
1912
0
    }
1913
0
}
1914
1915
/* speed()
1916
 * Luckily ALLYSTAR is only 8N1.
1917
 * Unluckily, no way to know what port we are on.
1918
 * So we assume UART0
1919
 */
1920
static bool speed(struct gps_device_t *session, speed_t speed,
1921
                  char parity UNUSED, int stopbits UNUSED)
1922
0
{
1923
0
    unsigned char msg[8] = {0};
1924
1925
0
    memset(msg, '\0', sizeof(msg));
1926
1927
0
    GPSD_LOG(LOG_WARN, &session->context->errout,
1928
0
             "ALLY: baudrate %u\n", (unsigned int) speed);
1929
1930
0
    if (0 == speed) {
1931
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
1932
0
                 "ALLY: invalid baudrate %u\n", (unsigned int) speed);
1933
0
        return 0;
1934
0
    }
1935
    // set CFG-PRT for UART0
1936
0
    msg[0] = 0x00;          // assume: UART0
1937
    // 1 to 3, reserved.
1938
0
    putle32(msg, 4, speed);
1939
0
    (void)ally_write(session, ALLY_CFG, CFG_PRT & 0x0ff, msg, 8);
1940
1941
0
    if ((speed_t)0 != session->context->fixed_port_speed) {
1942
        // save new speed as the fixed speed
1943
0
        session->context->fixed_port_speed = speed;
1944
0
    }
1945
    // change port speed to new speed.  Allystar is always 8N1
1946
0
    gpsd_set_speed(session, speed, 'N', 1);
1947
1948
0
    return true;
1949
0
}
1950
1951
static void event_hook(struct gps_device_t *session, event_t event)
1952
0
{
1953
0
    if (session->context->readonly) {
1954
0
        return;
1955
0
    }
1956
0
    if (event == EVENT_IDENTIFIED) {
1957
0
        GPSD_LOG(LOG_PROG, &session->context->errout, "ALLY: identified\n");
1958
1959
        // no longer set UBX-CFG-SBAS here, u-blox 9 and 10 do not have it
1960
1961
0
        if (session->context->passive) {
1962
            /* passive mode, do no autoconfig
1963
             * but we really want MON-VER. */
1964
0
            (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1965
0
        } else if (O_OPTIMIZE == session->mode) {
1966
0
            ally_mode(session, MODE_BINARY);
1967
            // restart init queue
1968
0
            session->queue = 0;
1969
0
        } else {
1970
            //* Turn off NMEA output, turn on UBX on this port.
1971
0
            ally_mode(session, MODE_NMEA);
1972
0
        }
1973
0
    } else if (event == EVENT_DEACTIVATE) {
1974
        /* There used to be a hotstart/reset here.
1975
         * That caused u-blox USB to re-enumerate.
1976
         * Sometimes to a new device name.
1977
         * Bad.  Don't do that anymore...
1978
         */
1979
0
    }
1980
0
}
1981
1982
static void init_query(struct gps_device_t *session)
1983
0
{
1984
    // MON-VER: query for version information
1985
    (void)ally_write(session, ALLY_MON, 0x04, NULL, 0);
1986
0
}
1987
1988
// this is everything we export
1989
// *INDENT-OFF*
1990
const struct gps_type_t driver_allystar =
1991
{
1992
    .type_name      = "ALLYSTAR",               // full name of type
1993
    .packet_type    = ALLYSTAR_PACKET,          // lexer packet type
1994
    .flags          = DRIVER_STICKY,            // remember this
1995
    .trigger        = NULL,                     // recognize the type
1996
    .channels       = 240,                      //  a guess
1997
    .probe_detect   = NULL,                     // no probe
1998
    .get_packet     = packet_get1,              // use generic one
1999
    .parse_packet   = parse_input,              // parse message packets
2000
    .rtcm_writer    = gpsd_write,               // send RTCM data straight
2001
    .init_query     = init_query,               // non-perturbing query
2002
    .event_hook     = event_hook,               // lifetime event handler
2003
    .speed_switcher = speed,                    // we can change baud rates
2004
    .mode_switcher  = NULL,                     // there is a mode switcher
2005
    .rate_switcher  = NULL,                     // change sample rate
2006
    .min_cycle.tv_sec  = 1,                     // default
2007
    .min_cycle.tv_nsec = 0,                     // default
2008
    .control_send   = control_send,             // how to send a control string
2009
    .time_offset    = NULL,                     // no NTP fudge factor
2010
};
2011
// *INDENT-ON*
2012
2013
// vim: set expandtab shiftwidth=4