Coverage Report

Created: 2026-02-26 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_skytraq.c
Line
Count
Source
1
/*
2
 * This is the gpsd driver for Skytraq GPSes operating in binary mode.
3
 *
4
 * SkyTraq is Big Endian
5
 *
6
 * This file is Copyright by the GPSD project
7
 * SPDX-License-Identifier: BSD-2-clause
8
 */
9
10
#include "../include/gpsd_config.h"  // must be before all includes
11
12
#include <ctype.h>
13
#include <math.h>
14
#include <stdbool.h>
15
#include <stdio.h>
16
#include <stdlib.h>
17
#include <string.h>       // for strlcpy()
18
#include <strings.h>
19
#include <unistd.h>
20
21
#include "../include/gpsd.h"
22
#include "../include/bits.h"
23
#include "../include/strfuncs.h"
24
#if defined(SKYTRAQ_ENABLE)
25
#include "../include/timespec.h"
26
27
#define HI(n)           ((n) >> 8)
28
#define LO(n)           ((n) & 0xff)
29
30
/*
31
 * No ACK/NAK?  Just retry after 6 seconds
32
 */
33
#define SKY_RETRY_TIME  6
34
// Phoenix has 230 channels
35
1.32k
#define SKY_CHANNELS    230      // max channels allowed in format
36
37
#ifdef __UNUSED
38
// Poll Software Version MID 2
39
static unsigned char versionprobe[] = {
40
    0xa0, 0xa1, 0x00, 0x02,
41
    0x02,               // MID 2
42
    0x01,               // System
43
    0x00, 0x0d, 0x0a
44
};
45
#endif  // __UNUSED
46
47
/* place checksum into msg, write to device
48
 * Return: number of bytes written
49
 *         negative on error
50
 */
51
static ssize_t sky_write(struct gps_device_t *session, char *msg,
52
                         const size_t data_len)
53
2.66k
{
54
2.66k
    uint8_t chk;
55
2.66k
    uint16_t len;
56
2.66k
    ssize_t i;
57
2.66k
    bool ok;
58
2.66k
    unsigned type = (unsigned)msg[4];
59
2.66k
    uint8_t buf[BUFSIZ];
60
2.66k
    uint8_t outbuf[BUFSIZ];
61
62
    // do not write if -b (readonly) option set
63
    // "passive" handled earlier
64
2.66k
    if (session->context->readonly) {
65
522
        return data_len;
66
522
    }
67
68
2.14k
    if (sizeof(buf) <= data_len) {
69
        // uh, oh;
70
0
        return -1;
71
0
    }
72
    // make a copy, so we can edit it
73
2.14k
    memcpy(buf, msg, data_len);
74
75
    // max length is undocumented, largest I could find is 261
76
2.14k
    len = (buf[2] << 8) | buf[3];
77
    // limit to 512 to pacify coverity
78
2.14k
    if (512 < len) {
79
0
        len = 512;
80
0
    }
81
2.14k
    if ((size_t)(len + 7) != data_len) {
82
        // uh, oh;
83
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
84
0
                 "Skytraq: Length error: len %u data_len %zu buf %s\n",
85
0
                 len, data_len,
86
0
                 gps_hexdump((char *)outbuf, sizeof(outbuf),
87
0
                             buf, data_len));
88
0
        return -2;
89
0
    }
90
91
    // calculate Checksum
92
2.14k
    chk = 0;
93
    // coverity_submit[tainted_data]
94
5.51k
    for (i = 0; i < len; i++) {
95
3.36k
        chk ^= buf[4 + i];
96
3.36k
    }
97
98
    // enter checksum after payload
99
2.14k
    buf[len + 4] = chk;
100
2.14k
    len += 7;
101
102
2.14k
    GPSD_LOG(LOG_IO, &session->context->errout,
103
2.14k
             "Skytraq: Writing control MID %02x: %s\n", type,
104
2.14k
             gps_hexdump((char *)outbuf, sizeof(outbuf), buf, len));
105
2.14k
    ok = gpsd_write(session, (const char *)buf, len) == len;
106
107
2.14k
    return ok;
108
2.14k
}
109
110
/* sky_mode() - NMEA/Binary mode changer
111
 * return: void
112
 *
113
 * Cherry-picked from:
114
 *  0004-Update-Skytraq-driver-to-support-Venus8-modules-chan.patch
115
 * by Kai Harrekilde-Petersen in:
116
 *  https://lists.gnu.org/archive/html/gpsd-dev/2018-03/msg00025.html
117
*/
118
static void sky_mode(struct gps_device_t *session, int mode)
119
0
{
120
0
    char msg[] = {
121
0
        0xA0, 0xA1,    // start-of-sentence sequence
122
0
        0x00, 0x03,
123
0
        0x09,          // SKY_CONFIG_MSG_TYPE
124
0
        0x00, 0x00, 0x00,
125
0
        0x0D, 0x0A     // end-of-sentence sequence
126
0
    };
127
0
    if (MODE_BINARY == mode) {
128
0
        msg[5] = 0x02;
129
0
    } else {                   // else to MODE_NMEA
130
0
        msg[5] = 0x01;
131
0
    }
132
133
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
134
0
             "Skytraq: setting MODE %s\n",
135
0
             (MODE_BINARY == mode) ? "Binary" : "NMEA");
136
0
    (void)sky_write(session, msg, 10);
137
0
}
138
139
/*
140
 * Convert PRN to gnssid and svid
141
 *
142
 * svId is always 0 to 99.
143
 */
144
static void PRN2_gnssId_svId(short PRN, uint8_t *gnssId, uint8_t *svId)
145
7.82k
{
146
    // fit into gnssid:svid
147
7.82k
    if (0 == PRN) {
148
        // skip 0 PRN
149
911
        *gnssId = 0;
150
911
        *svId = 0;
151
6.91k
    } else if ((1 <= PRN) &&
152
6.91k
               (32 >= PRN)) {
153
        // GPS
154
1.05k
        *gnssId = 0;
155
1.05k
        *svId = PRN;
156
5.86k
    } else if ((65 <= PRN) &&
157
5.03k
               (96 >= PRN)) {
158
        // GLONASS
159
1.20k
        *gnssId = 6;
160
1.20k
        *svId = PRN - 64;
161
4.66k
    } else if ((120 <= PRN) &&
162
3.26k
               (158 >= PRN)) {
163
        // SBAS
164
655
        *gnssId = 1;
165
655
        *svId = PRN;
166
4.00k
    } else if ((201 <= PRN) &&
167
2.01k
               (239 >= PRN)) {
168
        // BeiDou
169
764
        *gnssId = 3;
170
764
        *svId = PRN - 200;
171
3.24k
    } else if ((240 <= PRN) &&
172
1.25k
               (254 >= PRN)) {
173
        // IRNSS
174
571
        *gnssId = 20;
175
571
        *svId = PRN - 240;
176
2.67k
    } else {
177
        // huh?
178
2.67k
        *gnssId = 0;
179
2.67k
        *svId = 0;
180
2.67k
    }
181
7.82k
    return;
182
7.82k
}
183
184
/*
185
 decode MID 0x62 -- super packet
186
 *
187
 * Present in Phoenix
188
 */
189
static gps_mask_t sky_msg_62(struct gps_device_t *session,
190
                             const unsigned char *buf, const size_t len)
191
589
{
192
589
    unsigned sid;
193
589
    unsigned u[23];
194
589
    int i;
195
196
589
    if (3 > len) {
197
141
        GPSD_LOG(LOG_WARN, &session->context->errout,
198
141
                 "Skytraq 0x62: bad len %zu\n", len);
199
141
        return 0;
200
141
    }
201
202
448
    sid = getub(buf, 1);
203
448
    switch (sid) {
204
104
    case  0x80:
205
        // SBAS status
206
104
        if (8 < len) {
207
103
            GPSD_LOG(LOG_WARN, &session->context->errout,
208
103
                     "Skytraq 0x62/80: bad len %zu\n", len);
209
103
            return 0;
210
103
        }
211
7
        for (i = 0; i < 6; i++) {
212
6
            u[i] = getub(buf, i + 2);
213
6
        }
214
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
215
1
                 "Skytraq 0x62/80: enable %u ranging %u URA mask %u "
216
1
                 "correction %u chans %u subsystems %u \n",
217
1
                 u[0], u[1], u[2], u[3], u[4], u[5]);
218
1
        break;
219
35
    case  0x81:
220
        // QXSS status
221
35
        u[0] = getub(buf, 2);
222
35
        u[1] = getub(buf, 3);
223
35
        GPSD_LOG(LOG_PROG, &session->context->errout,
224
35
                 "Skytraq 0x62/81: enable %u chans %u\n",
225
35
                 u[0], u[1]);
226
35
        break;
227
70
    case  0x82:
228
        // SBAS advanced status
229
70
        if (46 < len) {
230
2
            GPSD_LOG(LOG_WARN, &session->context->errout,
231
2
                     "Skytraq 0x62/82: bad len %zu\n", len);
232
2
            return 0;
233
2
        }
234
1.56k
        for (i = 0; i < 22; i++) {
235
1.49k
            u[i] = getub(buf, i + 2);
236
1.49k
        }
237
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
238
68
                 "Skytraq 0x62/82: enable %u ranging %u URA %u corr %u "
239
68
                 "chans %u mask x%02x WAAS %u %u %u %u "
240
68
                 "EGNOS %u %u %u %u MSAS %u %u %u %u "
241
68
                 "GAGAN %u %u %u %u\n",
242
68
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
243
68
                 u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16], u[17],
244
68
                 u[18], u[19], u[20], u[21]);
245
68
        break;
246
239
    default:
247
239
        GPSD_LOG(LOG_PROG, &session->context->errout,
248
448
                 "Skytraq 0x62: SID x%02x len %zu\n", sid, len);
249
448
    }
250
343
    return 0;
251
448
}
252
253
/*
254
 * decode MID 0x63 -- super packet
255
 *
256
 * Present in Phoenix
257
 */
258
static gps_mask_t sky_msg_63(struct gps_device_t *session,
259
                             const unsigned char *buf, const size_t len)
260
696
{
261
696
    unsigned sid;
262
263
696
    if (3 > len) {
264
599
        GPSD_LOG(LOG_WARN, &session->context->errout,
265
599
                 "Skytraq 0x63: bad len %zu\n", len);
266
599
        return 0;
267
599
    }
268
269
97
    sid = getub(buf, 1);
270
271
    // FIXME: decode them!
272
97
    GPSD_LOG(LOG_PROG, &session->context->errout,
273
97
             "Skytraq 0x63: SID %u\n", sid);
274
97
    return 0;
275
696
}
276
277
/*
278
 * decode MID 0x64 -- super packet
279
 *
280
 * Present in Phoenix
281
 */
282
static gps_mask_t sky_msg_64(struct gps_device_t *session,
283
                             const unsigned char *buf, const size_t len)
284
1.05k
{
285
1.05k
    unsigned sid;
286
1.05k
    unsigned u[13];
287
1.05k
    int i;
288
1.05k
    int s[3];
289
290
1.05k
    if (3 > len) {
291
70
        GPSD_LOG(LOG_WARN, &session->context->errout,
292
70
                 "Skytraq 0x64: bad len %zu\n", len);
293
70
        return 0;
294
70
    }
295
296
984
    sid = getub(buf, 1);
297
984
    switch (sid) {
298
104
    case  0x80:
299
        // GNSS Boot status
300
104
        u[0] = getub(buf, 2);
301
104
        u[1] = getub(buf, 3);
302
104
        GPSD_LOG(LOG_PROG, &session->context->errout,
303
104
                 "Skytraq 0x64/80: enable %u type %u\n",
304
104
                 u[0], u[1]);
305
104
        break;
306
136
    case  0x81:
307
        // Extended NMEA Message Interval
308
136
        if (14 > len) {
309
68
            GPSD_LOG(LOG_WARN, &session->context->errout,
310
68
                     "Skytraq 0x64/81: bad len %zu\n", len);
311
68
            return 0;
312
68
        }
313
884
        for (i = 0; i < 12; i++) {
314
816
            u[i] = getub(buf, i + 2);
315
816
        }
316
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
317
68
                 "Skytraq 0x64/81: GGA %u GSA %u GSV %u GLL %u RMC %u "
318
68
                 "VTG %u ZDA %u GNS %u GBS %u GRS %u DTM %u GST %u\n",
319
68
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
320
68
                 u[9], u[10], u[11]);
321
68
        break;
322
69
    case  0x83:
323
        // Interference Detection Status
324
69
        u[0] = getub(buf, 2);
325
69
        u[1] = getub(buf, 3);
326
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
327
69
                 "Skytraq 0x64/83: enable %u status %u\n",
328
69
                 u[0], u[1]);
329
69
        break;
330
67
    case  0x85:
331
        // GPS PARAMETER SEARCH ENGINE NUMBER
332
67
        u[0] = getub(buf, 2);
333
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
334
67
                 "Skytraq 0x64/85: search engine number %u\n", u[0]);
335
67
        break;
336
70
    case  0x88:
337
        // Position/Fix navigation mask
338
70
        u[0] = getub(buf, 2);
339
70
        u[1] = getub(buf, 3);
340
70
        GPSD_LOG(LOG_PROG, &session->context->errout,
341
70
                 "Skytraq 0x64/88: 1st %u subsequent %u\n",
342
70
                 u[0], u[1]);
343
70
        break;
344
69
    case  0x8a:
345
        // GPS UTC Reference time
346
69
        if (7 > len) {
347
2
            GPSD_LOG(LOG_WARN, &session->context->errout,
348
2
                     "Skytraq 0x64/8a: bad len %zu\n", len);
349
2
            return 0;
350
2
        }
351
67
        u[0] = getub(buf, 2);
352
67
        u[1] = getbeu16(buf, 3);
353
67
        u[2] = getub(buf, 5);
354
67
        u[3] = getub(buf, 6);
355
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
356
67
                 "Skytraq 0x64/8a: enable %u year %u month %u day %u\n",
357
67
                 u[0], u[1], u[2], u[3]);
358
67
        break;
359
67
    case  0x8b:
360
        // GNSS Nav mode
361
67
        u[0] = getub(buf, 2);
362
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
363
67
                 "Skytraq 0x64/8b: mode %u\n", u[0]);
364
67
        break;
365
69
    case  0x8c:
366
        // GNSS Constellation type for nav solution
367
69
        u[0] = getbeu16(buf, 2);
368
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
369
69
                 "Skytraq 0x64/8c: Nav Type x%02x\n", u[0]);
370
69
        break;
371
72
    case  0x8e:
372
        // GPS time
373
72
        u[0] = getbeu32(buf, 2);    // TOW ms
374
72
        u[1] = getbeu32(buf, 6);    // TOW ns
375
72
        u[2] = getbeu16(buf, 10);   // GPS week
376
72
        s[0] = getsb(buf, 12);      // default leap s
377
72
        s[1] = getsb(buf, 13);      // current leap s
378
72
        u[3] = getub(buf, 14);      // valid
379
        // FIXME: save GPS week and leap s
380
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
381
72
                 "Skytraq 0x64/8a: TOW %u %u week %u leap %d %d valid x%x\n",
382
72
                 u[0], u[1], u[2], s[0], s[1], u[3]);
383
72
        break;
384
72
    case  0x92:
385
        // GLONASS Time corrections
386
72
        s[0] = getbes32(buf, 2);    // tau c
387
72
        s[1] = getbes32(buf, 6);    // tau gps
388
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
389
72
                 "Skytraq 0x64/92: tau c %d tau GPS %d\n",
390
72
                 s[0], s[1]);
391
72
        break;
392
67
    case  0xfe:
393
        // Version extension string
394
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
395
67
                 "Skytraq 0x64/fe: >%.32s<\n", &buf[2]);
396
67
        break;
397
122
    default:
398
122
        GPSD_LOG(LOG_PROG, &session->context->errout,
399
984
                 "Skytraq 0x64: SID x%02x len %zu\n", sid, len);
400
984
    }
401
914
    return 0;
402
984
}
403
404
/*
405
 * decode MID 0x65 -- super packet
406
 *
407
 * Present in Phoenix
408
 */
409
static gps_mask_t sky_msg_65(struct gps_device_t *session,
410
                             const unsigned char *buf, const size_t len)
411
339
{
412
339
    unsigned sid;
413
339
    unsigned u[13];
414
415
339
    if (3 > len) {
416
101
        GPSD_LOG(LOG_WARN, &session->context->errout,
417
101
                 "Skytraq 0x65: bad len %zu\n", len);
418
101
        return 0;
419
101
    }
420
421
238
    sid = getub(buf, 1);
422
238
    switch (sid) {
423
68
    case  0x80:
424
        // 1PPS Pulse width
425
68
        u[0] = getbeu32(buf, 2);    // pulse width miicro seconds
426
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
427
68
                 "Skytraq 0x65/80: width %u\n", u[0]);
428
68
        break;
429
68
    case  0x81:
430
        // PPS2 frequency
431
68
        u[0] = getbeu32(buf, 2);    // freq of PPS2 Hz
432
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
433
68
                 "Skytraq 0x65/81: PPS2 Hz %u\n", u[0]);
434
68
        break;
435
102
    default:
436
102
        GPSD_LOG(LOG_PROG, &session->context->errout,
437
238
                 "Skytraq 0x65: SID x%02x len %zu\n", sid, len);
438
238
    }
439
238
    return 0;
440
238
}
441
442
/*
443
 * decode MID 0x6A -- super packet
444
 *
445
 * Present in Phoenix
446
 */
447
static gps_mask_t sky_msg_6A(struct gps_device_t *session,
448
                             const unsigned char *buf, const size_t len)
449
300
{
450
300
    unsigned sid;
451
300
    unsigned u[13];
452
300
    double d[5];
453
454
300
    if (3 > len) {
455
158
        GPSD_LOG(LOG_WARN, &session->context->errout,
456
158
                 "Skytraq 0x6A: bad len %zu\n", len);
457
158
        return 0;
458
158
    }
459
460
142
    sid = getub(buf, 1);
461
142
    switch (sid) {
462
1
    case  0x83:
463
        // RTK mode and operational functioN
464
1
        u[0] = getub(buf, 2);                // RTK mode
465
1
        u[1] = getub(buf, 3);                // RTK function
466
1
        u[2] = getbeu32(buf, 4);             // saved survey length
467
1
        u[3] = getbeu32(buf, 8);             // standard deviation
468
1
        d[0] = getled64((const char *)buf, 12);    // latitude
469
1
        d[1] = getled64((const char *)buf, 20);    // longitude
470
1
        d[3] = getlef32((const char *)buf, 28);    // altitude (HAE or MSL?)
471
1
        u[4] = getub(buf, 32);               // runtime function
472
1
        u[5] = getbeu32(buf, 33);            // run-time survey length
473
1
        d[4] = getlef32((const char *)buf, 37);    // baseline length constant
474
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
475
1
                 "Skytraq 0x6A/83: mode %u func %u len %u sdev %u lat %.8f "
476
1
                 "lon %.8f alt %.4f func %u len %u len %.4f\n",
477
1
                 u[0], u[1], u[2], u[3], d[0], d[1], d[3], u[4], u[5], d[4]);
478
1
        break;
479
68
    case  0x85:
480
        // RTK slave base serial port baud ratE
481
68
        u[0] = getub(buf, 2);        // rate code
482
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
483
68
                 "Skytraq 0x6A/85: rate %u\n", u[0]);
484
68
        break;
485
1
    case  0x88:
486
        // RTK kinematic base serial port baud ratE
487
1
        u[0] = getub(buf, 2);        // rate code
488
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
489
1
                 "Skytraq 0x6A/88: rate %u\n", u[0]);
490
1
        break;
491
72
    default:
492
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
493
142
                 "Skytraq 0x6A: SID x%02x len %zu\n", sid, len);
494
142
    }
495
142
    return 0;
496
142
}
497
498
/*
499
 * decode MID 0x7A -- super packet
500
 *
501
 * Present in Phoenix
502
 */
503
static gps_mask_t sky_msg_7A(struct gps_device_t *session,
504
                             const unsigned char *buf, const size_t len)
505
327
{
506
327
    unsigned sid, ssid;
507
327
    unsigned u[16];
508
327
    double d[2];
509
327
    int i;
510
511
327
    if (3 > len) {
512
219
        GPSD_LOG(LOG_WARN, &session->context->errout,
513
219
                 "Skytraq 0x7A: bad len %zu\n", len);
514
219
        return 0;
515
219
    }
516
517
108
    sid = getub(buf, 1);
518
108
    ssid = getub(buf, 2);
519
108
    switch ((sid << 8) | ssid) {
520
2
    case 0x0e80:
521
        // Moving base software version
522
2
        if (16 > len) {
523
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
524
1
                     "Skytraq 0x7A/0E/80: bad len %zu\n", len);
525
1
            return 0;
526
1
        }
527
14
        for (i = 0; i < 13; i++) {
528
13
            u[i] = getub(buf, i + 3);
529
13
        }
530
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
531
1
                 "Skytraq 0x7A/0E/80: type %u "
532
1
                  "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u\n",
533
1
                 u[0], u[2], u[3], u[4], u[6], u[7], u[8], u[10],
534
1
                 u[11], u[12]);
535
1
        break;
536
1
    case 0x0e81:
537
        // Moving base software CRC
538
1
        u[0] = getub(buf, 3);
539
1
        u[1] = getbeu16(buf, 4);
540
541
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
542
1
                 "Skytraq  0x7A/0E/801: type %u crc %u\n", u[0], u[1]);
543
1
        break;
544
1
    case 0x0e82:
545
        // Moving base pos update rate
546
1
        u[0] = getub(buf, 3);
547
548
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
549
1
                 "Skytraq  0x7A/0E/802: rate %u\n", u[0]);
550
1
        break;
551
1
    case 0x0e83:
552
        // Moving base heading and pitch offsets
553
1
        d[0] = getbeu32(buf, 3);    // heading
554
1
        d[1] = getbeu32(buf, 7);    // pitch
555
556
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
557
1
                 "Skytraq  0x7A/0E/803: heading %f pitch %f\n", d[0], d[1]);
558
1
        break;
559
103
    default:
560
103
        GPSD_LOG(LOG_PROG, &session->context->errout,
561
108
                 "Skytraq 0x7A: SID x%02x/%02x len %zu\n", sid, ssid, len);
562
108
    }
563
107
    return 0;
564
108
}
565
566
/*
567
 * decode MID 0x80, Software Version
568
 *
569
 * 10 bytes
570
 *
571
 * Present in: Venus 6
572
 *             Venus 8
573
 *             Phoenix
574
 */
575
static gps_mask_t sky_msg_80(struct gps_device_t *session,
576
                             const unsigned char *buf, const size_t len)
577
171
{
578
171
    unsigned kver_x;  // kernel version
579
171
    unsigned kver_y;  // kernel version
580
171
    unsigned kver_z;  // kernel version
581
171
    unsigned over_x;  // ODM version
582
171
    unsigned over_y;  // ODM version
583
171
    unsigned over_z;  // ODM version
584
171
    unsigned rev_yy;  // revision
585
171
    unsigned rev_mm;  // revision
586
171
    unsigned rev_dd;  // revision
587
588
171
    if (14 != len) {
589
101
        return 0;
590
101
    }
591
592
70
    kver_x  = getbeu16(buf, 2);
593
70
    kver_y  = getub(buf, 4);
594
70
    kver_z  = getub(buf, 5);
595
70
    over_x  = getbeu16(buf, 6);
596
70
    over_y  = getub(buf, 8);
597
70
    over_z  = getub(buf, 9);
598
70
    rev_yy  = getbeu16(buf, 10);
599
70
    rev_mm  = getub(buf, 12);
600
70
    rev_dd  = getub(buf, 13);
601
602
70
    (void)snprintf(session->subtype, sizeof(session->subtype) - 1,
603
70
                   "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u",
604
70
                   kver_x, kver_y, kver_z,
605
70
                   over_x, over_y, over_z,
606
70
                   rev_yy, rev_mm, rev_dd);
607
608
70
    GPSD_LOG(LOG_PROG, &session->context->errout,
609
70
             "Skytraq 0x80: %s\n",
610
70
             session->subtype);
611
70
    return 0;
612
171
}
613
614
/*
615
 * decode MID 0x81 - Software CRC
616
 *
617
 * Present in Phoenix
618
 */
619
static gps_mask_t sky_msg_81(struct gps_device_t *session,
620
                             const unsigned char *buf, const size_t len)
621
68
{
622
68
    unsigned type, crc;
623
624
68
    if (4 != len) {
625
67
        GPSD_LOG(LOG_WARN, &session->context->errout,
626
67
                 "Skytraq 0x81: bad len %zu\n", len);
627
67
        return 0;
628
67
    }
629
630
1
    type = getub(buf, 1);
631
1
    crc  = getbeu16(buf, 2);
632
633
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
634
1
             "Skytraq 0x81: type %u crc %u\n", type, crc);
635
1
    return 0;
636
68
}
637
638
/*
639
 * decode MID 0x86 - Position Update Rate
640
 *
641
 * Present in Phoenix
642
 */
643
static gps_mask_t sky_msg_86(struct gps_device_t *session,
644
                             const unsigned char *buf, const size_t len)
645
168
{
646
168
    unsigned rate;
647
648
168
    if (2 != len) {
649
70
        GPSD_LOG(LOG_WARN, &session->context->errout,
650
70
                 "Skytraq 0x86: bad len %zu\n", len);
651
70
        return 0;
652
70
    }
653
654
98
    rate = getub(buf, 1);
655
656
98
    GPSD_LOG(LOG_PROG, &session->context->errout,
657
98
             "Skytraq 0x86: rate %u\n", rate);
658
98
    return 0;
659
168
}
660
661
/*
662
 * decode MID 0x89 - Binary measurement data output status
663
 *
664
 * Present in Phoenix
665
 */
666
static gps_mask_t sky_msg_89(struct gps_device_t *session,
667
                             const unsigned char *buf, const size_t len)
668
120
{
669
120
    unsigned i;
670
120
    uint8_t u[7];
671
672
120
    if (8 != len) {
673
119
        GPSD_LOG(LOG_WARN, &session->context->errout,
674
119
                 "Skytraq 0x89: bad len %zu\n", len);
675
119
        return 0;
676
119
    }
677
678
8
    for (i = 0; i < 7; i++) {
679
7
        u[i] = getub(buf, i + 1);
680
7
    }
681
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
682
1
             "Skytraq 0x89: rate %u Meas %u raw %u CH_status %u "
683
1
             "RCV_statas %u subf %u eraw %u\n",
684
1
             u[0], u[1], u[2], u[3], u[4], u[5], u[6]);
685
686
1
    return 0;
687
120
}
688
689
/*
690
 * decode MID 0x8A - Binary rtcm data output status
691
 *
692
 * Present in Phoenix
693
 */
694
static gps_mask_t sky_msg_8A(struct gps_device_t *session,
695
                             const unsigned char *buf, const size_t len)
696
489
{
697
489
    unsigned i;
698
489
    uint8_t u[15];
699
700
489
    if (16 != len) {
701
488
        GPSD_LOG(LOG_WARN, &session->context->errout,
702
488
                 "Skytraq 0x8A: bad len %zu\n", len);
703
488
        return 0;
704
488
    }
705
706
16
    for (i = 0; i < 15; i++) {
707
15
        u[i] = getub(buf, i + 1);
708
15
    }
709
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
710
1
             "Skytraq 0x8A: enable %u MSM %u 1005 %u 107x %u 108x %u "
711
1
             "109x %u 110x %u 111x %u 112x %u 1019 %u 1020 %u "
712
1
             "1042 %u 1046 %u type %u version %u\n",
713
1
             u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
714
1
             u[8], u[9], u[10], u[11], u[12], u[13], u[14]);
715
716
1
    return 0;
717
489
}
718
719
/*
720
 * decode MID 0x8B - Base position
721
 *
722
 * Present in Phoenix
723
 */
724
static gps_mask_t sky_msg_8B(struct gps_device_t *session,
725
                             const unsigned char *buf, const size_t len)
726
172
{
727
172
    uint8_t u[5];
728
172
    double d[3];
729
730
172
    if (35 != len) {
731
104
        GPSD_LOG(LOG_WARN, &session->context->errout,
732
104
                 "Skytraq 0x8B: bad len %zu\n", len);
733
104
        return 0;
734
104
    }
735
736
68
    u[0] = getub(buf, 1);
737
68
    u[1] = getbeu32(buf, 2);
738
68
    u[2] = getbeu32(buf, 6);
739
68
    d[0] = getbed64((const char *)buf, 10);
740
68
    d[1] = getbed64((const char *)buf, 18);
741
68
    d[2] = getbef32((const char *)buf, 26);
742
68
    u[3] = getub(buf, 30);
743
68
    u[4] = getbeu32(buf, 31);
744
745
68
    GPSD_LOG(LOG_PROG, &session->context->errout,
746
68
             "Skytraq 0x8B: saved mode %u saved length %u stddev %u "
747
68
             "lat %.9f lon %.9f HAE %0.4f run mode %u survey len %u\n",
748
68
             u[0], u[1], u[2], d[0], d[1], d[2], u[3], u[4]);
749
750
68
    return 0;
751
172
}
752
753
/*
754
 * decode MID 0x93 - NMEA Talker ID
755
 *
756
 * Present in Phoenix
757
 */
758
static gps_mask_t sky_msg_93(struct gps_device_t *session,
759
                             const unsigned char *buf, const size_t len)
760
154
{
761
154
    unsigned mode;
762
763
154
    if (2 != len) {
764
75
        GPSD_LOG(LOG_WARN, &session->context->errout,
765
75
                 "Skytraq 0x93: bad len %zu\n", len);
766
75
        return 0;
767
75
    }
768
769
79
    mode = getub(buf, 1);
770
771
79
    GPSD_LOG(LOG_PROG, &session->context->errout,
772
79
             "Skytraq 0x93: mode %u\n", mode);
773
79
    return 0;
774
154
}
775
776
/*
777
 * Binary Navigation Data Message (0xA8)
778
 * Returns 59 bytes;
779
 * Fix Mode, SV, Week, TOW, Latitude, Longitude, ellipsoid altitude,
780
 * mean sea level altitude, gdop, pdop, hdop, vdop, tdop,
781
 * ecef.{x,y,z}, ecef_v.{x,y,z}
782
 *
783
 * The following message (0xA8) is emitted by the modules supporting
784
 * the Binary Messages Raw Measurements Data Extension in AN0028
785
 * (https://www.skytraq.com.tw/homesite/AN0028.pdf)
786
 * Original implementation by Kai Harrekilde-Petersen
787
 * and adapted by Thatcher Chamberlin (j.thatcher.c@gmail.com)
788
 */
789
static gps_mask_t sky_msg_A8(struct gps_device_t *session,
790
                   const unsigned char *buf, const size_t len)
791
90
{
792
90
    unsigned char  navmode; // Navigation fix mode (0: No fix, 1: 2D, 2: 3D, 3: 3D+DGNSS
793
90
    unsigned short week;    // GNSS week number
794
90
    double   ftow;       // Time of week
795
90
    timespec_t ts_tow;
796
90
    char ts_buf[TIMESPEC_LEN];
797
798
90
    int *mode = &session->newdata.mode;
799
90
    int *status = &session->newdata.status;
800
90
    gps_mask_t mask = 0;
801
802
90
    if (59 != len) {
803
87
        GPSD_LOG(LOG_INF, &session->context->errout, "Skytraq: "
804
87
            "Navigation Data Message has incorrect length %zu\n", len);
805
87
        return 0;
806
87
    }
807
808
3
    navmode = getub(buf, 1);
809
3
    switch (navmode) {
810
1
    case 1: // 2D fix
811
1
        *mode = MODE_2D;
812
1
        *status = STATUS_GPS;
813
1
        break;
814
1
    case 2: // 3D fix
815
1
        *mode = MODE_3D;
816
1
        *status = STATUS_GPS;
817
1
        break;
818
0
    case 3: // 3D DGPS fix
819
0
        *mode = MODE_3D;
820
0
        *status = STATUS_DGPS;
821
0
        break;
822
823
1
    default: // Includes SKY_MODE_NONE
824
1
        *mode = MODE_NO_FIX;
825
1
        *status = STATUS_UNK;
826
1
        break;
827
3
    }
828
3
    mask |= MODE_SET | STATUS_SET;
829
830
3
    session->gpsdata.satellites_used = getub(buf, 2);
831
832
3
    mask |= ONLINE_SET;
833
3
    week = getbeu16(buf,  3);
834
3
    ftow = getbeu32(buf,  5) / 100.0;
835
3
    DTOTS(&ts_tow, (int)ftow);
836
837
3
    session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
838
3
    mask |= TIME_SET;
839
840
3
    if (MODE_2D == *mode || MODE_3D == *mode) {
841
2
        session->newdata.latitude  = getbes32(buf,  9) / 1e7;
842
2
        session->newdata.longitude = getbes32(buf, 13) / 1e7;
843
2
        mask |= LATLON_SET | NTPTIME_IS;
844
845
2
        if (MODE_3D == *mode) {
846
1
            session->newdata.altHAE  = getbes32(buf, 17) / 100.0;
847
1
            session->newdata.altMSL  = getbes32(buf, 21) / 100.0;
848
849
1
            session->newdata.ecef.x  = getbes32(buf, 35) / 100.0;
850
1
            session->newdata.ecef.y  = getbes32(buf, 39) / 100.0;
851
1
            session->newdata.ecef.z  = getbes32(buf, 43) / 100.0;
852
1
            session->newdata.ecef.vx = getbes32(buf, 47) / 100.0;
853
1
            session->newdata.ecef.vy = getbes32(buf, 51) / 100.0;
854
1
            session->newdata.ecef.vz = getbes32(buf, 55) / 100.0;
855
1
            mask |= ECEF_SET | VECEF_SET | ALTITUDE_SET;
856
1
        }
857
2
    }
858
859
3
    session->gpsdata.dop.gdop = getbeu16(buf, 25) / 100.0;
860
3
    session->gpsdata.dop.pdop = getbeu16(buf, 27) / 100.0;
861
3
    session->gpsdata.dop.hdop = getbeu16(buf, 29) / 100.0;
862
3
    session->gpsdata.dop.vdop = getbeu16(buf, 31) / 100.0;
863
3
    session->gpsdata.dop.tdop = getbeu16(buf, 33) / 100.0;
864
3
    mask |= DOP_SET | CLEAR_IS | REPORT_IS;
865
3
    GPSD_LOG(LOG_DATA, &session->context->errout,
866
3
         "Skytraq: NAVDATA time=%s, lat=%.7f lon=%.7f altHAE=%.2f altMSL=%.2f mode=%d status=%d "
867
3
         "gdop: %.2f, hdop: %.2f, pdop: %.2f, tdop: %.2f, vdop: %.2f\n",
868
3
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
869
3
         session->newdata.latitude,
870
3
         session->newdata.longitude,
871
3
         session->newdata.altHAE,
872
3
         session->newdata.altMSL,
873
3
         session->newdata.mode,
874
3
         session->newdata.status,
875
3
         session->gpsdata.dop.gdop,
876
3
         session->gpsdata.dop.hdop,
877
3
         session->gpsdata.dop.pdop,
878
3
         session->gpsdata.dop.tdop,
879
3
         session->gpsdata.dop.vdop);
880
3
    return mask;
881
3
}
882
883
/*
884
 * decode MID 0xAE - GNSS Datum
885
 *
886
 * Present in Phoenix
887
 */
888
static gps_mask_t sky_msg_AE(struct gps_device_t *session,
889
                             const unsigned char *buf, const size_t len)
890
1.02k
{
891
1.02k
    unsigned datum;
892
893
1.02k
    if (3 != len) {
894
867
        GPSD_LOG(LOG_WARN, &session->context->errout,
895
867
                 "Skytraq 0xAE: bad len %zu\n", len);
896
867
        return 0;
897
867
    }
898
899
156
    datum  = getbeu16(buf, 1);
900
901
156
    GPSD_LOG(LOG_PROG, &session->context->errout,
902
156
             "Skytraq 0xAE: datum %u\n", datum);
903
156
    return 0;
904
1.02k
}
905
906
/*
907
 * decode MID 0xaf - DOP mask
908
 *
909
 * Present in Phoenix
910
 */
911
static gps_mask_t sky_msg_AF(struct gps_device_t *session,
912
                             const unsigned char *buf, const size_t len)
913
139
{
914
139
    unsigned mode, pdop, hdop, gdop;
915
916
139
    if (8 != len) {
917
138
        GPSD_LOG(LOG_WARN, &session->context->errout,
918
138
                 "Skytraq 0xAF: bad len %zu\n", len);
919
138
        return 0;
920
138
    }
921
922
1
    mode = getub(buf, 1);
923
1
    pdop = getbeu16(buf, 2);
924
1
    hdop = getbeu16(buf, 4);
925
1
    gdop = getbeu16(buf, 6);
926
927
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
928
1
             "Skytraq 0xAF: Masks: mode %u pdop %u hdop %u gdop %u\n",
929
1
             mode, pdop, hdop, gdop);
930
1
    return 0;
931
139
}
932
933
/*
934
 * decode MID 0xb0 Elevation and DOP mask
935
 *
936
 * Present in Phoenix
937
 */
938
static gps_mask_t sky_msg_B0(struct gps_device_t *session,
939
                             const unsigned char *buf, const size_t len)
940
93
{
941
93
    unsigned select, elevation, cnr;
942
943
93
    if (4 != len) {
944
92
        GPSD_LOG(LOG_WARN, &session->context->errout,
945
92
                 "Skytraq 0xB0: bad len %zu\n", len);
946
92
        return 0;
947
92
    }
948
949
1
    select = getub(buf, 1);
950
1
    elevation = getub(buf, 2);
951
1
    cnr = getub(buf, 3);
952
953
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
954
1
             "Skytraq 0xB0: select %u el %u cnr %u\n",
955
1
             select, elevation, cnr);
956
1
    return 0;
957
93
}
958
959
/*
960
 * decode MID 0xb4 - Position Pinning Status
961
 *
962
 * Present in Phoenix
963
 */
964
static gps_mask_t sky_msg_B4(struct gps_device_t *session,
965
                             const unsigned char *buf, const size_t len)
966
79
{
967
79
    unsigned status, pspeed, pcnt, uspeed, ucnt, udist;
968
969
79
    if (12 != len) {
970
78
        GPSD_LOG(LOG_WARN, &session->context->errout,
971
78
                 "Skytraq 0xB4: bad len %zu\n", len);
972
78
        return 0;
973
78
    }
974
975
1
    status = getub(buf, 1);
976
1
    pspeed = getbeu16(buf, 2);
977
1
    pcnt = getbeu16(buf, 4);
978
1
    uspeed = getbeu16(buf, 6);
979
1
    ucnt = getbeu16(buf, 8);
980
1
    udist = getbeu16(buf, 10);
981
982
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
983
1
             "Skytraq 0xB4: status %u pspeed %u pcnt %u uspeed %u "
984
1
             "ucnt %u udist %u\n",
985
1
             status, pspeed, pcnt, uspeed, ucnt, udist);
986
1
    return 0;
987
79
}
988
989
/*
990
 * decode MID 0xB9 - Power Mode Status
991
 *
992
 * Present in Phoenix
993
 */
994
static gps_mask_t sky_msg_B9(struct gps_device_t *session,
995
                             const unsigned char *buf, const size_t len)
996
321
{
997
321
    unsigned mode;
998
999
321
    if (2 != len) {
1000
254
        GPSD_LOG(LOG_WARN, &session->context->errout,
1001
254
                 "Skytraq 0xB9: bad len %zu\n", len);
1002
254
        return 0;
1003
254
    }
1004
1005
67
    mode = getub(buf, 1);
1006
1007
67
    GPSD_LOG(LOG_PROG, &session->context->errout,
1008
67
             "Skytraq 0xB9: mode %u\n", mode);
1009
67
    return 0;
1010
321
}
1011
1012
/*
1013
 * decode MID 0xBB - 1PPS Cable Delay
1014
 *
1015
 * Present in Phoenix
1016
 */
1017
static gps_mask_t sky_msg_BB(struct gps_device_t *session,
1018
                             const unsigned char *buf, const size_t len)
1019
69
{
1020
69
    int delay;
1021
1022
69
    if (5 != len) {
1023
68
        GPSD_LOG(LOG_WARN, &session->context->errout,
1024
68
                 "Skytraq 0xBB: bad len %zu\n", len);
1025
68
        return 0;
1026
68
    }
1027
1028
1
    delay = getbeu32(buf, 1);
1029
1030
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
1031
1
             "Skytraq 0xBB: delay %d\n", delay);
1032
1
    return 0;
1033
69
}
1034
1035
/*
1036
 * decode MID 0xDC, Measurement Time
1037
 *
1038
 * 10 bytes
1039
 */
1040
static gps_mask_t sky_msg_DC(struct gps_device_t *session,
1041
                             const unsigned char *buf, const size_t len)
1042
271
{
1043
271
    unsigned iod;   // Issue of data 0 - 255
1044
271
    unsigned wn;    // week number 0 - 65535
1045
271
    unsigned tow;   // receiver tow 0 - 604799999 in mS
1046
271
    unsigned mp;    // measurement period 1 - 1000 ms
1047
271
    char ts_buf[TIMESPEC_LEN];
1048
271
    timespec_t ts_tow;
1049
1050
271
    if (10 != len) {
1051
194
        return 0;
1052
194
    }
1053
1054
77
    iod = (unsigned)getub(buf, 1);
1055
77
    wn = getbeu16(buf, 2);
1056
77
    tow = getbeu32(buf, 4);
1057
77
    mp = getbeu16(buf, 8);
1058
77
    MSTOTS(&ts_tow, tow);
1059
1060
    // should this be newdata.skyview_time?
1061
77
    session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, wn, ts_tow);
1062
1063
77
    GPSD_LOG(LOG_DATA, &session->context->errout,
1064
77
             "Skytraq 0xDC: iod %u wn %u tow %u mp %u t%s\n",
1065
77
             iod, wn, tow, mp,
1066
77
             timespec_str(&session->gpsdata.skyview_time, ts_buf,
1067
77
                          sizeof(ts_buf)));
1068
77
    return 0;
1069
271
}
1070
1071
/*
1072
 * decode MID 0xDD, Raw Measurements
1073
 *
1074
 */
1075
static gps_mask_t sky_msg_DD(struct gps_device_t *session,
1076
                             const unsigned char *buf, const size_t len)
1077
895
{
1078
895
    unsigned i;     // generic loop variable
1079
1080
    // buf[0] os the message ID
1081
    // Issue of data 0 - 255
1082
895
    unsigned iod = (unsigned)getub(buf, 1);
1083
    // number of measurements
1084
895
    unsigned nmeas = (unsigned)getub(buf, 2);
1085
1086
895
    GPSD_LOG(LOG_DATA, &session->context->errout,
1087
895
             "Skytraq 0xDD: iod %u, nmeas %u len %zd\n",
1088
895
             iod, nmeas, len);
1089
1090
895
    if (len < (3 + (nmeas * 23))) {
1091
109
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1092
109
                 "Skytraq 0xDD: bad len %zd, s/b %d\n",
1093
109
                 len, 3 + (nmeas & 23));
1094
109
        return 0;
1095
109
    }
1096
1097
    // check IOD?
1098
786
    session->gpsdata.raw.mtime = session->gpsdata.skyview_time;
1099
1100
    /* zero the measurement data
1101
     * so we can tell which meas never got set */
1102
786
    memset(session->gpsdata.raw.meas, 0, sizeof(session->gpsdata.raw.meas));
1103
1104
4.80k
    for (i = 0; i < nmeas; i++) {
1105
4.01k
        const char *obs_code;
1106
4.01k
        int off = 3 + (23 * i);
1107
1108
4.01k
        uint8_t PRN = getub(buf, off + 0);
1109
        // carrier-to-noise density ratio dB-Hz
1110
4.01k
        uint8_t cno = getub(buf, off + 1);
1111
        // pseudorange in meters
1112
4.01k
        double prMes = getbed64((const char *)buf, off + 2);
1113
        // carrier phase in cycles
1114
4.01k
        double cpMes = getbed64((const char *)buf, off + 10);
1115
        // doppler in Hz, positive towards sat
1116
4.01k
        double doMes = getbef32((const char *)buf, off + 18);
1117
1118
        /* tracking stat
1119
         * bit 0 - prMes valid
1120
         * bit 1 - doppler valid
1121
         * bit 2 - cpMes valid
1122
         * bit 3 - cp slip
1123
         * bit 4 - Coherent integration time?
1124
         */
1125
4.01k
        uint8_t trkStat = getub(buf, off + 22);
1126
4.01k
        uint8_t gnssId = 0;
1127
        // svId should always be from 0 to 99, except SBAS.
1128
4.01k
        uint8_t svId = 0;
1129
4.01k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1130
1131
4.01k
        session->gpsdata.raw.meas[i].gnssid = gnssId;
1132
4.01k
        switch (gnssId) {
1133
2.57k
        case 0:       // GPS
1134
2.57k
            FALLTHROUGH
1135
2.57k
        case 5:       // QZSS
1136
2.57k
            FALLTHROUGH
1137
2.71k
        case 20:      // IRNSS, just guessing here
1138
2.71k
            obs_code = "L1C";       // u-blox calls this L1C/A ?
1139
2.71k
            break;
1140
372
        case 1:       // SBAS
1141
372
            if (100 < svId) {
1142
                // Should be always true, but pacify Coverity 498045
1143
372
                svId -= 100;            // adjust for RINEX 3 svid
1144
372
            }
1145
372
            obs_code = "L1C";       // u-blox calls this L1C/A
1146
372
            break;
1147
0
        case 2:       // GALILEO
1148
0
            obs_code = "L1B";       // u-blox calls this E1OS
1149
0
            break;
1150
231
        case 3:       // BeiDou
1151
231
            obs_code = "L2I";       // u-blox calls this B1I
1152
231
            break;
1153
0
        default:      // huh?
1154
0
            FALLTHROUGH
1155
0
        case 4:       // IMES.  really?
1156
0
            obs_code = "";       // u-blox calls this L1
1157
0
            break;
1158
699
        case 6:       // GLONASS
1159
699
            obs_code = "L1C";       // u-blox calls this L1OF
1160
699
            break;
1161
4.01k
        }
1162
4.01k
        (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code,
1163
4.01k
                      sizeof(session->gpsdata.raw.meas[i].obs_code));
1164
1165
4.01k
        session->gpsdata.raw.meas[i].svid = svId;
1166
4.01k
        session->gpsdata.raw.meas[i].snr = cno;
1167
4.01k
        session->gpsdata.raw.meas[i].satstat = trkStat;
1168
4.01k
        if (trkStat & 1) {
1169
            // prMes valid
1170
1.78k
            session->gpsdata.raw.meas[i].pseudorange = prMes;
1171
2.23k
        } else {
1172
2.23k
            session->gpsdata.raw.meas[i].pseudorange = NAN;
1173
2.23k
        }
1174
4.01k
        if (trkStat & 2) {
1175
            // doppler valid
1176
2.01k
            session->gpsdata.raw.meas[i].doppler = doMes;
1177
2.01k
        } else {
1178
2.00k
            session->gpsdata.raw.meas[i].doppler = NAN;
1179
2.00k
        }
1180
4.01k
        if (trkStat & 4) {
1181
            // cpMes valid
1182
1.67k
            session->gpsdata.raw.meas[i].carrierphase = cpMes;
1183
2.34k
        } else {
1184
2.34k
            session->gpsdata.raw.meas[i].carrierphase = NAN;
1185
2.34k
        }
1186
4.01k
        session->gpsdata.raw.meas[i].codephase = NAN;
1187
4.01k
        session->gpsdata.raw.meas[i].deltarange = NAN;
1188
        // skytraq does not report locktime, so assume max
1189
4.01k
        session->gpsdata.raw.meas[i].locktime = LOCKMAX;
1190
4.01k
        if (trkStat & 8) {
1191
            // possible slip
1192
1.74k
            session->gpsdata.raw.meas[i].lli = 2;
1193
1.74k
        }
1194
4.01k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1195
4.01k
                 "PRN %u (%u:%u) prMes %f cpMes %f doMes %f\n"
1196
4.01k
                 "cno %u  rtkStat %u\n", PRN,
1197
4.01k
                 gnssId, svId, prMes, cpMes, doMes, cno, trkStat);
1198
1199
4.01k
    }
1200
1201
    // return RAW_IS;  // WIP
1202
786
    return 0;
1203
786
}
1204
1205
/*
1206
 * decode MID 0xDE, SV and channel status
1207
 *
1208
 * max payload: 3 + (Num_sats * 10) = 483 bytes
1209
 */
1210
static gps_mask_t sky_msg_DE(struct gps_device_t *session,
1211
                             const unsigned char *buf, const size_t len)
1212
1.56k
{
1213
1.56k
    int st, nsv;
1214
1.56k
    unsigned i;
1215
1.56k
    unsigned iod;   // Issue of data 0 - 255
1216
1.56k
    unsigned nsvs;  // number of SVs in this packet
1217
1218
1.56k
    if (3 > len) {
1219
246
        GPSD_LOG(LOG_WARN, &session->context->errout,
1220
246
                 "Skytraq 0xDE: bad len %zu\n", len);
1221
246
        return 0;
1222
246
    }
1223
1.32k
    iod = (unsigned)getub(buf, 1);
1224
1.32k
    nsvs = (unsigned)getub(buf, 2);
1225
    // too many sats?
1226
1.32k
    if (SKY_CHANNELS < nsvs) {
1227
67
        return 0;
1228
67
    }
1229
1230
1.25k
    if (len < (3 + (nsvs * 10))) {
1231
80
        GPSD_LOG(LOG_WARN, &session->context->errout,
1232
80
                 "Skytraq 0xDE: bad len %zu\n", len);
1233
80
        return 0;
1234
80
    }
1235
1.17k
    gpsd_zero_satellites(&session->gpsdata);
1236
5.37k
    for (i = st = nsv =  0; i < nsvs; i++) {
1237
4.20k
        int off = 3 + (10 * i);  // offset into buffer of start of this sat
1238
4.20k
        bool good;               // do we have a good record ?
1239
4.20k
        unsigned short sv_stat;
1240
4.20k
        unsigned short chan_stat;
1241
4.20k
        unsigned short ura;
1242
4.20k
        short PRN;
1243
4.20k
        uint8_t gnssId = 0;
1244
4.20k
        uint8_t svId = 0;
1245
1246
4.20k
        PRN = (short)getub(buf, off + 1);
1247
        // fit into gnssid:svid
1248
4.20k
        if (0 == PRN) {
1249
            // skip 0 PRN
1250
396
            continue;
1251
396
        }
1252
3.80k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1253
1254
3.80k
        session->gpsdata.skyview[st].gnssid = gnssId;
1255
3.80k
        session->gpsdata.skyview[st].svid = svId;
1256
3.80k
        session->gpsdata.skyview[st].PRN = PRN;
1257
1258
3.80k
        sv_stat = (unsigned short)getub(buf, off + 2);
1259
3.80k
        ura = (unsigned short)getub(buf, off + 3);
1260
3.80k
        session->gpsdata.skyview[st].ss = (double)getub(buf, off + 4);
1261
3.80k
        session->gpsdata.skyview[st].elevation =
1262
3.80k
            (double)getbes16(buf, off + 5);
1263
3.80k
        session->gpsdata.skyview[st].azimuth =
1264
3.80k
            (double)getbes16(buf, off + 7);
1265
3.80k
        chan_stat = (unsigned short)getub(buf, off + 9);
1266
1267
3.80k
        session->gpsdata.skyview[st].used = (bool)(chan_stat & 0x30);
1268
3.80k
        good = session->gpsdata.skyview[st].PRN != 0 &&
1269
3.80k
            session->gpsdata.skyview[st].azimuth != 0 &&
1270
3.50k
            session->gpsdata.skyview[st].elevation != 0;
1271
1272
3.80k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1273
3.80k
                 "Skytraq PRN=%2d El=%4.0f Az=%5.0f ss=%3.2f stat=%02x,%02x "
1274
3.80k
                 "ura=%d %c\n",
1275
3.80k
                 session->gpsdata.skyview[st].PRN,
1276
3.80k
                 session->gpsdata.skyview[st].elevation,
1277
3.80k
                 session->gpsdata.skyview[st].azimuth,
1278
3.80k
                 session->gpsdata.skyview[st].ss,
1279
3.80k
                 chan_stat, sv_stat, ura,
1280
3.80k
                 good ? '*' : ' ');
1281
1282
3.80k
        if (good) {
1283
3.21k
            st += 1;
1284
3.21k
            if (session->gpsdata.skyview[st].used) {
1285
21
                nsv++;
1286
21
            }
1287
3.21k
        }
1288
3.80k
    }
1289
1290
1.17k
    session->gpsdata.satellites_visible = st;
1291
1.17k
    if (MAXCHANNELS < session->gpsdata.satellites_visible) {
1292
4
        GPSD_LOG(LOG_WARN, &session->context->errout,
1293
4
                "Skytraq 0xDE: too many satellites %d\n",
1294
4
                 session->gpsdata.satellites_visible);
1295
4
        session->gpsdata.satellites_visible = MAXCHANNELS;
1296
4
    }
1297
1.17k
    session->gpsdata.satellites_used = nsv;
1298
1299
1.17k
    GPSD_LOG(LOG_DATA, &session->context->errout,
1300
1.17k
             "Skytraq 0xDE: nsvs=%u visible=%u iod=%u\n", nsvs,
1301
1.17k
             session->gpsdata.satellites_visible, iod);
1302
1.17k
    return SATELLITE_SET | USED_IS;
1303
1.25k
}
1304
1305
/*
1306
 * decode MID 0xDF, Nav status (PVT)
1307
 *
1308
 * 81 bytes
1309
 */
1310
static gps_mask_t sky_msg_DF(struct gps_device_t *session,
1311
                             const unsigned char *buf, const size_t len)
1312
81
{
1313
81
    unsigned short navstat;
1314
81
    unsigned iod;           // Issue of data 0 - 255
1315
81
    unsigned wn;            // week number 0 - 65535
1316
81
    double f_tow;           // receiver tow Sec
1317
81
    double clock_bias;
1318
81
    double clock_drift;
1319
81
    gps_mask_t mask = 0;
1320
81
    timespec_t ts_tow;
1321
81
    char ts_buf[TIMESPEC_LEN];
1322
1323
81
    if (81 != len) {
1324
76
        return 0;
1325
76
    }
1326
1327
5
    iod = (unsigned)getub(buf, 1);
1328
1329
    // fix status is byte 2
1330
5
    navstat = (unsigned short)getub(buf, 2);
1331
5
    session->newdata.status = STATUS_UNK;
1332
5
    session->newdata.mode = MODE_NO_FIX;
1333
5
    switch (navstat) {
1334
0
    case 1:
1335
        // fix prediction, ignore
1336
0
        break;
1337
0
    case 2:
1338
0
        session->newdata.status = STATUS_GPS;
1339
0
        session->newdata.mode = MODE_2D;
1340
0
        break;
1341
1
    case 3:
1342
1
        session->newdata.status = STATUS_GPS;
1343
1
        session->newdata.mode = MODE_3D;
1344
1
        break;
1345
0
    case 4:
1346
0
        session->newdata.status = STATUS_DGPS;
1347
0
        session->newdata.mode = MODE_3D;
1348
0
        break;
1349
4
    default:
1350
4
        break;
1351
5
    }
1352
1353
5
    wn = getbeu16(buf, 3);
1354
5
    f_tow = getbed64((const char *)buf, 5);
1355
5
    DTOTS(&ts_tow, f_tow);
1356
1357
    // position/velocity is bytes 13-48, meters and m/s
1358
5
    session->newdata.ecef.x = (double)getbed64((const char *)buf, 13),
1359
5
    session->newdata.ecef.y = (double)getbed64((const char *)buf, 21),
1360
5
    session->newdata.ecef.z = (double)getbed64((const char *)buf, 29),
1361
5
    session->newdata.ecef.vx = (double)getbef32((const char *)buf, 37),
1362
5
    session->newdata.ecef.vy = (double)getbef32((const char *)buf, 41),
1363
5
    session->newdata.ecef.vz = (double)getbef32((const char *)buf, 45);
1364
5
    mask |= ECEF_SET | VECEF_SET;
1365
1366
5
    clock_bias = getbed64((const char *)buf, 49);
1367
5
    clock_drift = getbes32(buf, 57);
1368
1369
5
    session->gpsdata.dop.gdop = getbef32((const char *)buf, 61);
1370
5
    session->gpsdata.dop.pdop = getbef32((const char *)buf, 65);
1371
5
    session->gpsdata.dop.hdop = getbef32((const char *)buf, 69);
1372
5
    session->gpsdata.dop.vdop = getbef32((const char *)buf, 73);
1373
5
    session->gpsdata.dop.tdop = getbef32((const char *)buf, 77);
1374
5
    mask |= DOP_SET;
1375
1376
5
    session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow );
1377
1378
5
    GPSD_LOG(LOG_DATA, &session->context->errout,
1379
5
             "Skytraq 0xDF: iod=%u, stat=%u, wn=%u, tow=%f, t=%s "
1380
5
             "cb: %f, cd: %f "
1381
5
             "gdop: %.2f, pdop: %.2f, hdop: %.2f, vdop: %.2f, tdop: %.2f\n",
1382
5
             iod, navstat, wn, f_tow,
1383
5
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1384
5
             clock_bias, clock_drift,
1385
5
             session->gpsdata.dop.gdop,
1386
5
             session->gpsdata.dop.pdop,
1387
5
             session->gpsdata.dop.hdop,
1388
5
             session->gpsdata.dop.vdop,
1389
5
             session->gpsdata.dop.tdop);
1390
1391
5
    mask |= TIME_SET | STATUS_SET | MODE_SET | CLEAR_IS | REPORT_IS;
1392
5
    return mask;
1393
5
}
1394
1395
/*
1396
 * decode MID 0xE0, GPS Subframe data
1397
 *
1398
 * len 33 bytes
1399
 *
1400
 */
1401
static gps_mask_t sky_msg_E0(struct gps_device_t *session,
1402
                             const unsigned char *buf, const size_t len)
1403
330
{
1404
330
    int i;
1405
330
    unsigned prn;   // GPS sat PRN
1406
330
    unsigned subf;  // subframe 1-5
1407
    // the words are preprocessed, not raw, just the 24bits of data
1408
330
    uint32_t words[10];  // subframe 1-5
1409
1410
330
    if (33 != len) {
1411
75
        return 0;
1412
75
    }
1413
1414
255
    prn = (unsigned)getub(buf, 1);
1415
255
    subf = (unsigned)getub(buf, 2);
1416
2.80k
    for (i = 0; i < 10; i++) {
1417
2.55k
        words[i] = (uint32_t)getbeu24(buf, 3 + (i * 3));
1418
2.55k
    }
1419
1420
255
    GPSD_LOG(LOG_DATA, &session->context->errout,
1421
255
             "Skytraq 0xE0: prn=%u, subf=%u\n",
1422
255
             prn, subf);
1423
1424
    // could be SBAS?
1425
255
    return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words);
1426
330
}
1427
1428
/*
1429
 * pretend to decode MID 0xE2, Beiduo D1 Subframe data
1430
 *
1431
 * from Beidou Standard BDS-SIS-ICD-2.0
1432
 * D1, with the data rate of 50 bps, is broadcasted by the MEO/IGSO satellites
1433
 *
1434
 * len 31 bytes
1435
 *
1436
 */
1437
static gps_mask_t sky_msg_E2(struct gps_device_t *session,
1438
                             const unsigned char *buf, const size_t len)
1439
72
{
1440
72
    int i;
1441
72
    unsigned prn;   // BeidouPS sat PRN 206-214
1442
72
    unsigned subf;  // subframe 1-5
1443
    // the words are preprocessed, not raw, just the 28 bytes of data
1444
72
    uint8_t bytes[28];  // raw data
1445
1446
72
    if (31 != len) {
1447
71
        return 0;
1448
71
    }
1449
1450
1
    prn = (unsigned)getub(buf, 1);
1451
1
    subf = (unsigned)getub(buf, 2);
1452
29
    for (i = 0; i < 28; i++) {
1453
28
        bytes[i] = getub(buf, 3 + i);
1454
28
    }
1455
1456
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
1457
1
             "Skytraq Beidou D1 subframe PRN %d Subframe %d "
1458
1
             "length %zd byte:%s\n",
1459
1
             prn, subf, len,
1460
1
             gps_hexdump(session->msgbuf, sizeof(session->msgbuf),
1461
1
                         bytes, 28));
1462
1463
1
    return ONLINE_SET;
1464
72
}
1465
1466
/*
1467
 * pretend to decode MID 0xE3, Beiduo D2 Subframe data
1468
 *
1469
 * from Beidou Standard BDS-SIS-ICD-2.0
1470
 * D2, with the data rate of 500 bps, is broadcasted by the GEO satellites.
1471
 *
1472
 * len 31 bytes
1473
 *
1474
 */
1475
static gps_mask_t sky_msg_E3(struct gps_device_t *session,
1476
                             const unsigned char *buf, const size_t len)
1477
304
{
1478
304
    int i;
1479
304
    unsigned prn;   // BeidouPS sat PRN 201-205
1480
304
    unsigned subf;  // subframe 1-5
1481
    // the words are preprocessed, not raw, just the 28 bytes of data
1482
304
    uint8_t bytes[28];  // raw data
1483
1484
304
    if (31 != len) {
1485
303
        return 0;
1486
303
    }
1487
1488
1
    prn = (unsigned)getub(buf, 1);
1489
1
    subf = (unsigned)getub(buf, 2);
1490
29
    for (i = 0; i < 28; i++) {
1491
28
        bytes[i] = getub(buf, 3 + i);
1492
28
    }
1493
1494
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
1495
1
             "Skytraq Beidou D2 subframe PRN %d Subframe %d "
1496
1
             "length %zd byte:%s\n",
1497
1
             prn, subf, len,
1498
1
             gps_hexdump(session->msgbuf, sizeof(session->msgbuf),
1499
1
                         bytes, 28));
1500
1501
1
    return ONLINE_SET;
1502
304
}
1503
1504
1505
static gps_mask_t sky_parse(struct gps_device_t * session,
1506
                            unsigned char *buf, size_t len)
1507
12.2k
{
1508
12.2k
    gps_mask_t mask = 0;
1509
1510
12.2k
    if (0 == len) {
1511
0
        return mask;
1512
0
    }
1513
1514
12.2k
    buf += 4;   // skip the leaders and length
1515
12.2k
    len -= 7;   // don't count the leaders, length, csum and terminators
1516
    // session->driver.sirf.lastid = buf[0];
1517
1518
    // check the checksum??
1519
1520
    // could change if the set of messages we enable does
1521
    // session->cycle_end_reliable = true;
1522
1523
12.2k
    switch (buf[0]) {
1524
589
    case 0x62:
1525
589
        mask = sky_msg_62(session, buf, len);
1526
589
        break;
1527
696
    case 0x63:
1528
696
        mask = sky_msg_63(session, buf, len);
1529
696
        break;
1530
1.05k
    case 0x64:
1531
1.05k
        mask = sky_msg_64(session, buf, len);
1532
1.05k
        break;
1533
339
    case 0x65:
1534
339
        mask = sky_msg_65(session, buf, len);
1535
339
        break;
1536
300
    case 0x6A:
1537
300
        mask = sky_msg_6A(session, buf, len);
1538
300
        break;
1539
327
    case 0x7A:
1540
327
        mask = sky_msg_7A(session, buf, len);
1541
327
        break;
1542
171
    case 0x80:
1543
        // 128
1544
171
        mask = sky_msg_80(session, buf, len);
1545
171
        break;
1546
68
    case 0x81:
1547
        // Software CRC
1548
68
        mask = sky_msg_81(session, buf, len);
1549
68
        break;
1550
713
    case 0x83:
1551
        // 131 - ACK
1552
713
        if (2 == len) {
1553
67
            GPSD_LOG(LOG_PROG, &session->context->errout,
1554
67
                     "Skytraq 0x83: ACK MID %#02x\n", buf[1]);
1555
646
        } else if (3 == len) {
1556
69
            GPSD_LOG(LOG_PROG, &session->context->errout,
1557
69
                     "Skytraq 0x83: ACK MID %#02x/%02x\n", buf[1], buf[2]);
1558
577
        } else if (4 <= len) {
1559
452
            GPSD_LOG(LOG_PROG, &session->context->errout,
1560
452
                     "Skytraq 0x83: ACK MID %#02x/%02x/%02x\n",
1561
452
                     buf[1], buf[2], buf[3]);
1562
452
        } else {
1563
125
            GPSD_LOG(LOG_PROG, &session->context->errout,
1564
125
                     "Skytraq 0x83: ACK\n");
1565
125
        }
1566
713
        break;
1567
448
    case 0x84:
1568
        // 132 - NACK
1569
448
        if (2 == len) {
1570
69
            GPSD_LOG(LOG_INF, &session->context->errout,
1571
69
                     "Skytraq 0x84: NACK MID %#02x\n", buf[1]);
1572
379
        } else if (3 == len) {
1573
98
            GPSD_LOG(LOG_INF, &session->context->errout,
1574
98
                     "Skytraq 0x84: NACK MID %#02x/%02x\n", buf[1], buf[2]);
1575
281
        } else if (4 <= len) {
1576
69
            GPSD_LOG(LOG_INF, &session->context->errout,
1577
69
                     "Skytraq 0x84: NACK MID %#02x/%02x/x%02x\n",
1578
69
                     buf[1], buf[2], buf[3]);
1579
212
        } else {
1580
212
            GPSD_LOG(LOG_INF, &session->context->errout,
1581
212
                     "Skytraq 0x84: NACK\n");
1582
212
        }
1583
448
        break;
1584
168
    case 0x86:
1585
        // 134 Position Update Rate
1586
168
        mask = sky_msg_86(session, buf, len);
1587
168
        break;
1588
120
    case 0x89:
1589
120
        mask = sky_msg_89(session, buf, len);
1590
120
        break;
1591
489
    case 0x8A:
1592
489
        mask = sky_msg_8A(session, buf, len);
1593
489
        break;
1594
172
    case 0x8B:
1595
172
        mask = sky_msg_8B(session, buf, len);
1596
172
        break;
1597
154
    case 0x93:
1598
        // NMEA TALKER id
1599
154
        mask = sky_msg_93(session, buf, len);
1600
154
        break;
1601
1602
90
    case 0xA8:
1603
        // Navigation Data Message
1604
90
        mask = sky_msg_A8(session, buf, len);
1605
90
        break;
1606
1607
1.02k
    case 0xae:
1608
        // GNSS Datum
1609
1.02k
        mask = sky_msg_AE(session, buf, len);
1610
1.02k
        break;
1611
1612
139
    case 0xaf:
1613
        // DOP Mask
1614
139
        mask = sky_msg_AF(session, buf, len);
1615
139
        break;
1616
1617
93
    case 0xb0:
1618
        // Elevation and CNR mask
1619
93
        mask = sky_msg_B0(session, buf, len);
1620
93
        break;
1621
1622
79
    case 0xb4:
1623
        // Position Pinning Status
1624
79
        mask = sky_msg_B4(session, buf, len);
1625
79
        break;
1626
1627
321
    case 0xb9:
1628
321
        mask = sky_msg_B9(session, buf, len);
1629
321
        break;
1630
1631
69
    case 0xbb:
1632
69
        mask = sky_msg_BB(session, buf, len);
1633
69
        break;
1634
1635
271
    case 0xDC:
1636
        // 220
1637
271
        mask = sky_msg_DC(session, buf, len);
1638
271
        break;
1639
1640
1.03k
    case 0xDD:
1641
        // 221
1642
1.03k
        if (3 <= len) {
1643
895
            mask = sky_msg_DD(session, buf, len);
1644
895
        }
1645
1.03k
        break;
1646
1647
1.56k
    case 0xDE:
1648
        // 222
1649
1.56k
        mask = sky_msg_DE(session, buf, len);
1650
1.56k
        break;
1651
1652
81
    case 0xDF:
1653
        // 223 - Nave status (PVT)
1654
81
        mask = sky_msg_DF(session, buf, len);
1655
81
        break;
1656
1657
330
    case 0xE0:
1658
        // 224
1659
330
        mask = sky_msg_E0(session, buf, len);
1660
330
        break;
1661
1662
72
    case 0xE2:
1663
        // 226 - Beidou2 D1 Subframe data
1664
72
        mask = sky_msg_E2(session, buf, len);
1665
72
        break;
1666
1667
304
    case 0xE3:
1668
        // 227 - Beidou2 D2 Subframe data
1669
304
        mask = sky_msg_E3(session, buf, len);
1670
304
        break;
1671
1672
134
    case 0x67:   // sub-id messages
1673
134
        FALLTHROUGH
1674
234
    case 0x6F:   // sub-id messages
1675
234
        GPSD_LOG(LOG_PROG, &session->context->errout,
1676
234
                 "Skytraq Unknown MID x%02x SID x%02x length %zd\n",
1677
234
                 buf[0], buf[1], len);
1678
234
        break;
1679
718
    default:
1680
718
        GPSD_LOG(LOG_PROG, &session->context->errout,
1681
718
                 "Skytraq Unknown MID %#02x length %zd\n",
1682
718
                 buf[0], len);
1683
718
        break;
1684
12.2k
    }
1685
12.2k
    return mask;
1686
12.2k
}
1687
1688
static gps_mask_t skybin_parse_input(struct gps_device_t *session)
1689
14.1k
{
1690
    /*
1691
     * Use this hook to step, slowly, through the init messages.
1692
     * By sending only one for each three received we try
1693
     * to avoid overrunning the receiver input buffer.
1694
     */
1695
1696
14.1k
    if (UINT_MAX != session->cfg_stage) {
1697
8.05k
        session->cfg_step++;
1698
8.05k
    }
1699
1700
14.1k
    if (UINT_MAX != session->cfg_stage &&
1701
8.05k
        3 <= session->cfg_step) {
1702
        // more init to do
1703
1704
2.68k
        session->cfg_stage++;
1705
2.68k
        session->cfg_step = 0;
1706
1707
        // Note: the checksums in the Skytaq doc are sometimes wrong...
1708
1709
        // drivers/driver_nmea0183.c send 0x04 to get MID 0x80 on detect
1710
1711
        // FIXME: make a table
1712
2.68k
        switch (session->cfg_stage) {
1713
132
        case 1:
1714
            // Send MID 0x3, to get back MID 0x81 Software CRC
1715
132
            (void)sky_write(session, "\xA0\xA1\x00\x02\x03\x00\x03\x0d\x0a",
1716
132
                            9);
1717
132
            break;
1718
119
        case 2:
1719
            // Send MID 0x10, to get back MID 0x86 (Position Update Rate)
1720
119
            (void)sky_write(session, "\xA0\xA1\x00\x01\x10\x10\x0d\x0a", 8);
1721
119
            break;
1722
141
        case 3:
1723
            // Send MID 0x15, to get back MID 0xB9 (Power Mode Status)
1724
141
            (void)sky_write(session, "\xA0\xA1\x00\x01\x15\x15\x0d\x0a", 8);
1725
141
            break;
1726
112
        case 4:
1727
            // Send MID 0x1f, to get back MID 0x89 Measurement data statuS
1728
112
            (void)sky_write(session, "\xA0\xA1\x00\x01\x1f\x1f\x0d\x0a", 8);
1729
112
            break;
1730
116
        case 5:
1731
            // Send MID 0x21, to get back MID 0x8a  RTCM Data output status
1732
116
            (void)sky_write(session, "\xA0\xA1\x00\x01\x21\x21\x0d\x0a", 8);
1733
116
            break;
1734
110
        case 6:
1735
            // Send MID 0x23, to get back MID 0x8B (Base Position)
1736
110
            (void)sky_write(session, "\xA0\xA1\x00\x01\x23\x23\x0d\x0a", 8);
1737
110
            break;
1738
98
        case 7:
1739
            // Send MID 0x2d, to get back MID 0xAE (GNSS Datum)
1740
98
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2d\x2d\x0d\x0a", 8);
1741
98
            break;
1742
103
        case 8:
1743
            // Send MID 0x2E, to get back MID 0xAF (DOP Mask)
1744
103
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2e\x2e\x0d\x0a", 8);
1745
103
            break;
1746
96
        case 9:
1747
            // Send MID 0x2F, to get back MID 0x80 Elevation and SNR mask
1748
96
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2f\x2f\x0d\x0a", 8);
1749
96
            break;
1750
92
        case 10:
1751
            // Send MID 0x3a, to get back MID 0xb4 Position Pinning
1752
92
            (void)sky_write(session, "\xA0\xA1\x00\x01\x3a\x3a\x0d\x0a", 8);
1753
92
            break;
1754
90
        case 11:
1755
            // Send MID 0x44, to get back MID 0xc2 1PPS timing
1756
            // Timing mode versions only
1757
90
            (void)sky_write(session, "\xA0\xA1\x00\x01\x44\x44\x0d\x0a", 8);
1758
90
            break;
1759
101
        case 12:
1760
            // Send MID 0x46, to get back MID 0xbb 1PPS delay
1761
101
            (void)sky_write(session, "\xA0\xA1\x00\x01\x46\x46\x0d\x0a", 8);
1762
101
            break;
1763
95
        case 13:
1764
            // Send MID 0x4f, to get back MID 0x93 NMEA talker ID
1765
95
            (void)sky_write(session, "\xA0\xA1\x00\x01\x4f\x4f\x0d\x0a", 8);
1766
95
            break;
1767
86
        case 14:
1768
            // Send MID 0x56, to get back MID 0xc3 1PPS Output Mode
1769
            // Timing mode versions only
1770
86
            (void)sky_write(session, "\xA0\xA1\x00\x01\x56\x56\x0d\x0a", 8);
1771
86
            break;
1772
81
        case 15:
1773
            // Send MID 0x62/02, to get back MID 0x62/80 SBAS status
1774
81
            (void)sky_write(session,
1775
81
                            "\xA0\xA1\x00\x02\x62\x02\x60\x0d\x0a", 9);
1776
81
            break;
1777
76
        case 16:
1778
            // Send MID 0x62/04, to get back MID 0x62/81 QZSS status
1779
76
            (void)sky_write(session,
1780
76
                            "\xA0\xA1\x00\x02\x62\x04\x66\x0d\x0a", 9);
1781
76
            break;
1782
72
        case 17:
1783
            // Send MID 0x62/06, to get back MID 0x62/82 SBAS Advanced status
1784
72
            (void)sky_write(session,
1785
72
                            "\xA0\xA1\x00\x02\x62\x06\x64\x0d\x0a", 9);
1786
72
            break;
1787
65
        case 18:
1788
            // Send MID 0x63/02, to get back MID 0x62/80 SAEE Status
1789
            // not on PX1172RH_DS
1790
65
            (void)sky_write(session,
1791
65
                            "\xA0\xA1\x00\x02\x63\x02\x61\x0d\x0a", 9);
1792
65
            break;
1793
58
        case 19:
1794
            // Send MID 0x64/01, to get back MID 0x64/80
1795
58
            (void)sky_write(session,
1796
58
                            "\xA0\xA1\x00\x02\x64\x01\x65\x0d\x0a", 9);
1797
58
            break;
1798
53
        case 20:
1799
            // Send MID 0x64/03, to get back MID 0x64/81
1800
53
            (void)sky_write(session,
1801
53
                            "\xA0\xA1\x00\x02\x64\x03\x67\x0d\x0a", 9);
1802
53
            break;
1803
51
        case 21:
1804
            // Send MID 0x64/07, to get back MID 0x64/83
1805
51
            (void)sky_write(session,
1806
51
                            "\xA0\xA1\x00\x02\x64\x07\x63\x0d\x0a", 9);
1807
51
            break;
1808
46
        case 22:
1809
            // Send MID 0x64/0b, to get back MID 0x64/85
1810
46
            (void)sky_write(session,
1811
46
                            "\xA0\xA1\x00\x02\x64\x0b\x6f\x0d\x0a", 9);
1812
46
            break;
1813
43
        case 23:
1814
            // Send MID 0x64/12, to get back MID 0x64/88
1815
43
            (void)sky_write(session,
1816
43
                            "\xA0\xA1\x00\x02\x64\x12\x76\x0d\x0a", 9);
1817
43
            break;
1818
41
        case 24:
1819
            // Send MID 0x64/16, to get back MID 0x64/8a
1820
41
            (void)sky_write(session,
1821
41
                            "\xA0\xA1\x00\x02\x64\x16\x72\x0d\x0a", 9);
1822
41
            break;
1823
41
        case 25:
1824
            // Send MID 0x64/18, to get back MID 0x64/8b
1825
41
            (void)sky_write(session,
1826
41
                            "\xA0\xA1\x00\x02\x64\x18\x7c\x0d\x0a", 9);
1827
41
            break;
1828
40
        case 26:
1829
            // Send MID 0x64/1a, to get back MID 0x64/8c
1830
40
            (void)sky_write(session,
1831
40
                            "\xA0\xA1\x00\x02\x64\x1a\x7e\x0d\x0a", 9);
1832
40
            break;
1833
39
        case 27:
1834
            // Send MID 0x64/20, to get back MID 0x64/8e
1835
39
            (void)sky_write(session,
1836
39
                            "\xA0\xA1\x00\x02\x64\x20\x44\x0d\x0a", 9);
1837
39
            break;
1838
38
        case 28:
1839
            // Send MID 0x64/22, to get back MID 0x64/8f
1840
            // not on PX1172RH_DS
1841
38
            (void)sky_write(session,
1842
38
                            "\xA0\xA1\x00\x02\x64\x22\x46\x0d\x0a", 9);
1843
38
            break;
1844
37
        case 29:
1845
            // Send MID 0x64/28, to get back MID 0x64/92
1846
37
            (void)sky_write(session,
1847
37
                            "\xA0\xA1\x00\x02\x64\x28\x4c\x0d\x0a", 9);
1848
37
            break;
1849
35
        case 30:
1850
            // Send MID 0x64/30, to get back MID 0x64/98
1851
            // not on PX1172RH_DS
1852
35
            (void)sky_write(session,
1853
35
                            "\xA0\xA1\x00\x02\x64\x30\x54\x0d\x0a", 9);
1854
35
            break;
1855
34
        case 31:
1856
            // Send MID 0x64/31, to get back MID 0x64/
1857
            // not on PX1172RH_DS
1858
34
            (void)sky_write(session,
1859
34
                            "\xA0\xA1\x00\x02\x64\x31\x55\x0d\x0a", 9);
1860
34
            break;
1861
33
        case 32:
1862
            // Send MID 0x64/7d, to get back MID 0x64/fe
1863
33
            (void)sky_write(session,
1864
33
                            "\xA0\xA1\x00\x02\x64\x7d\x19\x0d\x0a", 9);
1865
33
            break;
1866
32
        case 33:
1867
            // Send MID 0x64/35, to get back MID 0x64/99
1868
            // not on PX1172RH_DS
1869
32
            (void)sky_write(session,
1870
32
                            "\xA0\xA1\x00\x03\x64\x35\x01\x50\x0d\x0a", 10);
1871
32
            break;
1872
29
        case 34:
1873
            // Send MID 0x64/36, to get back MID 0x64/9a
1874
            // not on PX1172RH_DS
1875
29
            (void)sky_write(session,
1876
29
                            "\xA0\xA1\x00\x02\x64\x36\x52\x0d\x0a", 9);
1877
29
            break;
1878
26
        case 35:
1879
            // Send MID 0x64/3c, to get back MID 0x64/99
1880
            // not on PX1172RH_DS
1881
26
            (void)sky_write(session,
1882
26
                            "\xA0\xA1\x00\x04\x64\x3c\x47\x47\x19\x0d\x0a",
1883
26
                            11);
1884
26
            break;
1885
24
        case 36:
1886
            // Send MID 0x65/02, to get back MID 0x64/80
1887
24
            (void)sky_write(session,
1888
24
                            "\xA0\xA1\x00\x02\x65\x02\x67\x0d\x0a", 9);
1889
24
            break;
1890
23
        case 37:
1891
            // Send MID 0x65/04, to get back MID 0x64/8f
1892
23
            (void)sky_write(session,
1893
23
                            "\xA0\xA1\x00\x02\x65\x04\x61\x0d\x0a", 9);
1894
23
            break;
1895
22
        case 38:
1896
            // Send MID 0x6a/02, to get back MID 0x6a/83
1897
22
            (void)sky_write(session,
1898
22
                            "\xA0\xA1\x00\x02\x6a\x02\x68\x0d\x0a", 9);
1899
22
            break;
1900
21
        case 39:
1901
            // Send MID 0x6a/07, to get back MID 0x6a/83
1902
21
            (void)sky_write(session,
1903
21
                            "\xA0\xA1\x00\x02\x6a\x07\x6d\x0d\x0a", 9);
1904
21
            break;
1905
19
        case 40:
1906
            // Send MID 0x6a/0d, to get back MID 0x6a/85
1907
19
            (void)sky_write(session,
1908
19
                            "\xA0\xA1\x00\x02\x6a\x0d\x67\x0d\x0a", 9);
1909
19
            break;
1910
19
        case 41:
1911
            // Send MID 0x6a/14, to get back MID 0x6a/86
1912
19
            (void)sky_write(session,
1913
19
                            "\xA0\xA1\x00\x02\x6a\x14\xfd\x0d\x0a", 9);
1914
19
            break;
1915
18
        case 42:
1916
            // Send MID 0x6a/16, to get back MID 0x6a/89
1917
            // not on PX1172RH_DS ?
1918
18
            (void)sky_write(session,
1919
18
                            "\xA0\xA1\x00\x02\x6a\x16\x7c\x0d\x0a", 9);
1920
18
            break;
1921
16
        case 43:
1922
            // Send MID 0x7a/0e/01, to get back MID 0x7a/0e/80
1923
            // not on PX1172RH_DS ?
1924
16
            (void)sky_write(session,
1925
16
                            "\xA0\xA1\x00\x03\x7a\x0e\x01\x75\x0d\x0a", 10);
1926
16
            break;
1927
15
        case 44:
1928
            // Send MID 0x7a/0e/02, to get back MID 0x7a/0e/81
1929
            // not on PX1172RH_DS ?
1930
15
            (void)sky_write(session,
1931
15
                            "\xA0\xA1\x00\x03\x7a\x0e\x02\x76\x0d\x0a", 10);
1932
15
            break;
1933
14
        case 45:
1934
            // Send MID 0x7a/0e/03, to get back MID 0x7a/0e/82
1935
            // not on PX1172RH_DS ?
1936
14
            (void)sky_write(session,
1937
14
                            "\xA0\xA1\x00\x03\x7a\x0e\x03\x77\x0d\x0a", 10);
1938
14
            break;
1939
14
        case 46:
1940
            // Send MID 0x7a/0e/05, to get back MID 0x7a/0e/83
1941
            // not on PX1172RH_DS ?
1942
14
            (void)sky_write(session,
1943
14
                            "\xA0\xA1\x00\x03\x7a\x0e\x05\x71\x0d\x0a", 10);
1944
14
            break;
1945
14
        default:
1946
            // Done
1947
14
            session->cfg_stage = UINT_MAX;
1948
14
            break;
1949
2.68k
        }
1950
2.68k
    }
1951
1952
14.1k
    if (SKY_PACKET == session->lexer.type) {
1953
12.2k
        return  sky_parse(session, session->lexer.outbuffer,
1954
12.2k
                        session->lexer.outbuflen);
1955
12.2k
    }
1956
1.96k
    if (NMEA_PACKET == session->lexer.type) {
1957
1.96k
        return nmea_parse((char *)session->lexer.outbuffer, session);
1958
1.96k
    }
1959
    // should not get here...
1960
1961
0
    return 0;
1962
1.96k
}
1963
1964
// this is everything we export
1965
// *INDENT-OFF*
1966
const struct gps_type_t driver_skytraq =
1967
{
1968
    .channels       = SKY_CHANNELS,          // consumer-grade GPS
1969
    .control_send   = sky_write,             // how to send a control string
1970
    .event_hook     = NULL,                  // lifetime event handler
1971
    .flags          = DRIVER_STICKY,         // remember this
1972
    .get_packet     = packet_get1,           // be prepared for Skytraq or NMEA
1973
    .init_query     = NULL,                  // non-perturbing initial query
1974
    .mode_switcher  = sky_mode,              // Mode switcher
1975
    .packet_type    = SKY_PACKET,            // associated lexer packet type
1976
    .parse_packet   = skybin_parse_input,    // parse message packets
1977
    .probe_detect   = NULL,                  // no probe
1978
    .rtcm_writer    = gpsd_write,            // send RTCM data straight
1979
    .trigger        = NULL,                  // no trigger
1980
    .type_name      = "Skytraq",             // full name of type
1981
};
1982
// *INDENT-ON*
1983
#endif  // defined SKYTRAQ_ENABLE)
1984
1985
// vim: set expandtab shiftwidth=4