Coverage Report

Created: 2026-04-15 06:11

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.13k
#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
3.31k
{
54
3.31k
    uint8_t chk;
55
3.31k
    uint16_t len;
56
3.31k
    ssize_t i;
57
3.31k
    bool ok;
58
3.31k
    unsigned type = (unsigned)msg[4];
59
3.31k
    uint8_t buf[BUFSIZ];
60
3.31k
    uint8_t outbuf[BUFSIZ];
61
62
    // do not write if -b (readonly) option set
63
    // "passive" handled earlier
64
3.31k
    if (session->context->readonly) {
65
879
        return data_len;
66
879
    }
67
68
2.43k
    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.43k
    memcpy(buf, msg, data_len);
74
75
    // max length is undocumented, largest I could find is 261
76
2.43k
    len = (buf[2] << 8) | buf[3];
77
    // limit to 512 to pacify coverity
78
2.43k
    if (512 < len) {
79
0
        len = 512;
80
0
    }
81
2.43k
    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.43k
    chk = 0;
93
    // coverity_submit[tainted_data]
94
6.00k
    for (i = 0; i < len; i++) {
95
3.57k
        chk ^= buf[4 + i];
96
3.57k
    }
97
98
    // enter checksum after payload
99
2.43k
    buf[len + 4] = chk;
100
2.43k
    len += 7;
101
102
2.43k
    GPSD_LOG(LOG_IO, &session->context->errout,
103
2.43k
             "Skytraq: Writing control MID %02x: %s\n", type,
104
2.43k
             gps_hexdump((char *)outbuf, sizeof(outbuf), buf, len));
105
2.43k
    ok = gpsd_write(session, (const char *)buf, len) == len;
106
107
2.43k
    return ok;
108
2.43k
}
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
6.25k
{
146
    // fit into gnssid:svid
147
6.25k
    if (0 == PRN) {
148
        // skip 0 PRN
149
597
        *gnssId = 0;
150
597
        *svId = 0;
151
5.66k
    } else if ((1 <= PRN) &&
152
5.66k
               (32 >= PRN)) {
153
        // GPS
154
772
        *gnssId = 0;
155
772
        *svId = PRN;
156
4.89k
    } else if ((65 <= PRN) &&
157
4.16k
               (96 >= PRN)) {
158
        // GLONASS
159
1.08k
        *gnssId = 6;
160
1.08k
        *svId = PRN - 64;
161
3.80k
    } else if ((120 <= PRN) &&
162
2.67k
               (158 >= PRN)) {
163
        // SBAS
164
517
        *gnssId = 1;
165
517
        *svId = PRN;
166
3.28k
    } else if ((201 <= PRN) &&
167
1.82k
               (239 >= PRN)) {
168
        // BeiDou
169
629
        *gnssId = 3;
170
629
        *svId = PRN - 200;
171
2.65k
    } else if ((240 <= PRN) &&
172
1.19k
               (254 >= PRN)) {
173
        // IRNSS
174
566
        *gnssId = 20;
175
566
        *svId = PRN - 240;
176
2.08k
    } else {
177
        // huh?
178
2.08k
        *gnssId = 0;
179
2.08k
        *svId = 0;
180
2.08k
    }
181
6.25k
    return;
182
6.25k
}
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
640
{
192
640
    unsigned sid;
193
640
    unsigned u[23];
194
640
    int i;
195
196
640
    if (3 > len) {
197
163
        GPSD_LOG(LOG_WARN, &session->context->errout,
198
163
                 "Skytraq 0x62: bad len %zu\n", len);
199
163
        return 0;
200
163
    }
201
202
477
    sid = getub(buf, 1);
203
477
    switch (sid) {
204
104
    case  0x80:
205
        // SBAS status
206
104
        if (8 < len) {
207
69
            GPSD_LOG(LOG_WARN, &session->context->errout,
208
69
                     "Skytraq 0x62/80: bad len %zu\n", len);
209
69
            return 0;
210
69
        }
211
245
        for (i = 0; i < 6; i++) {
212
210
            u[i] = getub(buf, i + 2);
213
210
        }
214
35
        GPSD_LOG(LOG_PROG, &session->context->errout,
215
35
                 "Skytraq 0x62/80: enable %u ranging %u URA mask %u "
216
35
                 "correction %u chans %u subsystems %u \n",
217
35
                 u[0], u[1], u[2], u[3], u[4], u[5]);
218
35
        break;
219
68
    case  0x81:
220
        // QXSS status
221
68
        u[0] = getub(buf, 2);
222
68
        u[1] = getub(buf, 3);
223
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
224
68
                 "Skytraq 0x62/81: enable %u chans %u\n",
225
68
                 u[0], u[1]);
226
68
        break;
227
149
    case  0x82:
228
        // SBAS advanced status
229
149
        if (46 < len) {
230
71
            GPSD_LOG(LOG_WARN, &session->context->errout,
231
71
                     "Skytraq 0x62/82: bad len %zu\n", len);
232
71
            return 0;
233
71
        }
234
1.79k
        for (i = 0; i < 22; i++) {
235
1.71k
            u[i] = getub(buf, i + 2);
236
1.71k
        }
237
78
        GPSD_LOG(LOG_PROG, &session->context->errout,
238
78
                 "Skytraq 0x62/82: enable %u ranging %u URA %u corr %u "
239
78
                 "chans %u mask x%02x WAAS %u %u %u %u "
240
78
                 "EGNOS %u %u %u %u MSAS %u %u %u %u "
241
78
                 "GAGAN %u %u %u %u\n",
242
78
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
243
78
                 u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16], u[17],
244
78
                 u[18], u[19], u[20], u[21]);
245
78
        break;
246
156
    default:
247
156
        GPSD_LOG(LOG_PROG, &session->context->errout,
248
477
                 "Skytraq 0x62: SID x%02x len %zu\n", sid, len);
249
477
    }
250
337
    return 0;
251
477
}
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
503
{
261
503
    unsigned sid;
262
263
503
    if (3 > len) {
264
382
        GPSD_LOG(LOG_WARN, &session->context->errout,
265
382
                 "Skytraq 0x63: bad len %zu\n", len);
266
382
        return 0;
267
382
    }
268
269
121
    sid = getub(buf, 1);
270
271
    // FIXME: decode them!
272
121
    GPSD_LOG(LOG_PROG, &session->context->errout,
273
121
             "Skytraq 0x63: SID %u\n", sid);
274
121
    return 0;
275
503
}
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.02k
{
285
1.02k
    unsigned sid;
286
1.02k
    unsigned u[13];
287
1.02k
    int i;
288
1.02k
    int s[3];
289
290
1.02k
    if (3 > len) {
291
39
        GPSD_LOG(LOG_WARN, &session->context->errout,
292
39
                 "Skytraq 0x64: bad len %zu\n", len);
293
39
        return 0;
294
39
    }
295
296
984
    sid = getub(buf, 1);
297
984
    switch (sid) {
298
69
    case  0x80:
299
        // GNSS Boot status
300
69
        u[0] = getub(buf, 2);
301
69
        u[1] = getub(buf, 3);
302
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
303
69
                 "Skytraq 0x64/80: enable %u type %u\n",
304
69
                 u[0], u[1]);
305
69
        break;
306
105
    case  0x81:
307
        // Extended NMEA Message Interval
308
105
        if (14 > len) {
309
40
            GPSD_LOG(LOG_WARN, &session->context->errout,
310
40
                     "Skytraq 0x64/81: bad len %zu\n", len);
311
40
            return 0;
312
40
        }
313
845
        for (i = 0; i < 12; i++) {
314
780
            u[i] = getub(buf, i + 2);
315
780
        }
316
65
        GPSD_LOG(LOG_PROG, &session->context->errout,
317
65
                 "Skytraq 0x64/81: GGA %u GSA %u GSV %u GLL %u RMC %u "
318
65
                 "VTG %u ZDA %u GNS %u GBS %u GRS %u DTM %u GST %u\n",
319
65
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
320
65
                 u[9], u[10], u[11]);
321
65
        break;
322
51
    case  0x83:
323
        // Interference Detection Status
324
51
        u[0] = getub(buf, 2);
325
51
        u[1] = getub(buf, 3);
326
51
        GPSD_LOG(LOG_PROG, &session->context->errout,
327
51
                 "Skytraq 0x64/83: enable %u status %u\n",
328
51
                 u[0], u[1]);
329
51
        break;
330
69
    case  0x85:
331
        // GPS PARAMETER SEARCH ENGINE NUMBER
332
69
        u[0] = getub(buf, 2);
333
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
334
69
                 "Skytraq 0x64/85: search engine number %u\n", u[0]);
335
69
        break;
336
123
    case  0x88:
337
        // Position/Fix navigation mask
338
123
        u[0] = getub(buf, 2);
339
123
        u[1] = getub(buf, 3);
340
123
        GPSD_LOG(LOG_PROG, &session->context->errout,
341
123
                 "Skytraq 0x64/88: 1st %u subsequent %u\n",
342
123
                 u[0], u[1]);
343
123
        break;
344
71
    case  0x8a:
345
        // GPS UTC Reference time
346
71
        if (7 > len) {
347
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
348
1
                     "Skytraq 0x64/8a: bad len %zu\n", len);
349
1
            return 0;
350
1
        }
351
70
        u[0] = getub(buf, 2);
352
70
        u[1] = getbeu16(buf, 3);
353
70
        u[2] = getub(buf, 5);
354
70
        u[3] = getub(buf, 6);
355
70
        GPSD_LOG(LOG_PROG, &session->context->errout,
356
70
                 "Skytraq 0x64/8a: enable %u year %u month %u day %u\n",
357
70
                 u[0], u[1], u[2], u[3]);
358
70
        break;
359
76
    case  0x8b:
360
        // GNSS Nav mode
361
76
        u[0] = getub(buf, 2);
362
76
        GPSD_LOG(LOG_PROG, &session->context->errout,
363
76
                 "Skytraq 0x64/8b: mode %u\n", u[0]);
364
76
        break;
365
76
    case  0x8c:
366
        // GNSS Constellation type for nav solution
367
76
        u[0] = getbeu16(buf, 2);
368
76
        GPSD_LOG(LOG_PROG, &session->context->errout,
369
76
                 "Skytraq 0x64/8c: Nav Type x%02x\n", u[0]);
370
76
        break;
371
67
    case  0x8e:
372
        // GPS time
373
67
        u[0] = getbeu32(buf, 2);    // TOW ms
374
67
        u[1] = getbeu32(buf, 6);    // TOW ns
375
67
        u[2] = getbeu16(buf, 10);   // GPS week
376
67
        s[0] = getsb(buf, 12);      // default leap s
377
67
        s[1] = getsb(buf, 13);      // current leap s
378
67
        u[3] = getub(buf, 14);      // valid
379
        // FIXME: save GPS week and leap s
380
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
381
67
                 "Skytraq 0x64/8a: TOW %u %u week %u leap %d %d valid x%x\n",
382
67
                 u[0], u[1], u[2], s[0], s[1], u[3]);
383
67
        break;
384
36
    case  0x92:
385
        // GLONASS Time corrections
386
36
        s[0] = getbes32(buf, 2);    // tau c
387
36
        s[1] = getbes32(buf, 6);    // tau gps
388
36
        GPSD_LOG(LOG_PROG, &session->context->errout,
389
36
                 "Skytraq 0x64/92: tau c %d tau GPS %d\n",
390
36
                 s[0], s[1]);
391
36
        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
174
    default:
398
174
        GPSD_LOG(LOG_PROG, &session->context->errout,
399
984
                 "Skytraq 0x64: SID x%02x len %zu\n", sid, len);
400
984
    }
401
943
    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
505
{
412
505
    unsigned sid;
413
505
    unsigned u[13];
414
415
505
    if (3 > len) {
416
219
        GPSD_LOG(LOG_WARN, &session->context->errout,
417
219
                 "Skytraq 0x65: bad len %zu\n", len);
418
219
        return 0;
419
219
    }
420
421
286
    sid = getub(buf, 1);
422
286
    switch (sid) {
423
69
    case  0x80:
424
        // 1PPS Pulse width
425
69
        u[0] = getbeu32(buf, 2);    // pulse width miicro seconds
426
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
427
69
                 "Skytraq 0x65/80: width %u\n", u[0]);
428
69
        break;
429
72
    case  0x81:
430
        // PPS2 frequency
431
72
        u[0] = getbeu32(buf, 2);    // freq of PPS2 Hz
432
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
433
72
                 "Skytraq 0x65/81: PPS2 Hz %u\n", u[0]);
434
72
        break;
435
145
    default:
436
145
        GPSD_LOG(LOG_PROG, &session->context->errout,
437
286
                 "Skytraq 0x65: SID x%02x len %zu\n", sid, len);
438
286
    }
439
286
    return 0;
440
286
}
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
744
{
450
744
    unsigned sid;
451
744
    unsigned u[13];
452
744
    double d[5];
453
454
744
    if (3 > len) {
455
259
        GPSD_LOG(LOG_WARN, &session->context->errout,
456
259
                 "Skytraq 0x6A: bad len %zu\n", len);
457
259
        return 0;
458
259
    }
459
460
485
    sid = getub(buf, 1);
461
485
    switch (sid) {
462
115
    case  0x83:
463
        // RTK mode and operational functioN
464
115
        u[0] = getub(buf, 2);                // RTK mode
465
115
        u[1] = getub(buf, 3);                // RTK function
466
115
        u[2] = getbeu32(buf, 4);             // saved survey length
467
115
        u[3] = getbeu32(buf, 8);             // standard deviation
468
115
        d[0] = getled64((const char *)buf, 12);    // latitude
469
115
        d[1] = getled64((const char *)buf, 20);    // longitude
470
115
        d[3] = getlef32((const char *)buf, 28);    // altitude (HAE or MSL?)
471
115
        u[4] = getub(buf, 32);               // runtime function
472
115
        u[5] = getbeu32(buf, 33);            // run-time survey length
473
115
        d[4] = getlef32((const char *)buf, 37);    // baseline length constant
474
115
        GPSD_LOG(LOG_PROG, &session->context->errout,
475
115
                 "Skytraq 0x6A/83: mode %u func %u len %u sdev %u lat %.8f "
476
115
                 "lon %.8f alt %.4f func %u len %u len %.4f\n",
477
115
                 u[0], u[1], u[2], u[3], d[0], d[1], d[3], u[4], u[5], d[4]);
478
115
        break;
479
150
    case  0x85:
480
        // RTK slave base serial port baud ratE
481
150
        u[0] = getub(buf, 2);        // rate code
482
150
        GPSD_LOG(LOG_PROG, &session->context->errout,
483
150
                 "Skytraq 0x6A/85: rate %u\n", u[0]);
484
150
        break;
485
101
    case  0x88:
486
        // RTK kinematic base serial port baud ratE
487
101
        u[0] = getub(buf, 2);        // rate code
488
101
        GPSD_LOG(LOG_PROG, &session->context->errout,
489
101
                 "Skytraq 0x6A/88: rate %u\n", u[0]);
490
101
        break;
491
119
    default:
492
119
        GPSD_LOG(LOG_PROG, &session->context->errout,
493
485
                 "Skytraq 0x6A: SID x%02x len %zu\n", sid, len);
494
485
    }
495
485
    return 0;
496
485
}
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
253
{
506
253
    unsigned sid, ssid;
507
253
    unsigned u[16];
508
253
    double d[2];
509
253
    int i;
510
511
253
    if (3 > len) {
512
149
        GPSD_LOG(LOG_WARN, &session->context->errout,
513
149
                 "Skytraq 0x7A: bad len %zu\n", len);
514
149
        return 0;
515
149
    }
516
517
104
    sid = getub(buf, 1);
518
104
    ssid = getub(buf, 2);
519
104
    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
99
    default:
560
99
        GPSD_LOG(LOG_PROG, &session->context->errout,
561
104
                 "Skytraq 0x7A: SID x%02x/%02x len %zu\n", sid, ssid, len);
562
104
    }
563
103
    return 0;
564
104
}
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
254
{
578
254
    unsigned kver_x;  // kernel version
579
254
    unsigned kver_y;  // kernel version
580
254
    unsigned kver_z;  // kernel version
581
254
    unsigned over_x;  // ODM version
582
254
    unsigned over_y;  // ODM version
583
254
    unsigned over_z;  // ODM version
584
254
    unsigned rev_yy;  // revision
585
254
    unsigned rev_mm;  // revision
586
254
    unsigned rev_dd;  // revision
587
588
254
    if (14 != len) {
589
188
        return 0;
590
188
    }
591
592
66
    kver_x  = getbeu16(buf, 2);
593
66
    kver_y  = getub(buf, 4);
594
66
    kver_z  = getub(buf, 5);
595
66
    over_x  = getbeu16(buf, 6);
596
66
    over_y  = getub(buf, 8);
597
66
    over_z  = getub(buf, 9);
598
66
    rev_yy  = getbeu16(buf, 10);
599
66
    rev_mm  = getub(buf, 12);
600
66
    rev_dd  = getub(buf, 13);
601
602
66
    (void)snprintf(session->subtype, sizeof(session->subtype) - 1,
603
66
                   "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u",
604
66
                   kver_x, kver_y, kver_z,
605
66
                   over_x, over_y, over_z,
606
66
                   rev_yy, rev_mm, rev_dd);
607
608
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
609
66
             "Skytraq 0x80: %s\n",
610
66
             session->subtype);
611
66
    return 0;
612
254
}
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
94
{
622
94
    unsigned type, crc;
623
624
94
    if (4 != len) {
625
70
        GPSD_LOG(LOG_WARN, &session->context->errout,
626
70
                 "Skytraq 0x81: bad len %zu\n", len);
627
70
        return 0;
628
70
    }
629
630
24
    type = getub(buf, 1);
631
24
    crc  = getbeu16(buf, 2);
632
633
24
    GPSD_LOG(LOG_PROG, &session->context->errout,
634
24
             "Skytraq 0x81: type %u crc %u\n", type, crc);
635
24
    return 0;
636
94
}
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
146
{
646
146
    unsigned rate;
647
648
146
    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
76
    rate = getub(buf, 1);
655
656
76
    GPSD_LOG(LOG_PROG, &session->context->errout,
657
76
             "Skytraq 0x86: rate %u\n", rate);
658
76
    return 0;
659
146
}
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
168
{
669
168
    unsigned i;
670
168
    uint8_t u[7];
671
672
168
    if (8 != len) {
673
92
        GPSD_LOG(LOG_WARN, &session->context->errout,
674
92
                 "Skytraq 0x89: bad len %zu\n", len);
675
92
        return 0;
676
92
    }
677
678
608
    for (i = 0; i < 7; i++) {
679
532
        u[i] = getub(buf, i + 1);
680
532
    }
681
76
    GPSD_LOG(LOG_PROG, &session->context->errout,
682
76
             "Skytraq 0x89: rate %u Meas %u raw %u CH_status %u "
683
76
             "RCV_statas %u subf %u eraw %u\n",
684
76
             u[0], u[1], u[2], u[3], u[4], u[5], u[6]);
685
686
76
    return 0;
687
168
}
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
645
{
697
645
    unsigned i;
698
645
    uint8_t u[15];
699
700
645
    if (16 != len) {
701
644
        GPSD_LOG(LOG_WARN, &session->context->errout,
702
644
                 "Skytraq 0x8A: bad len %zu\n", len);
703
644
        return 0;
704
644
    }
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
645
}
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
181
{
727
181
    uint8_t u[5];
728
181
    double d[3];
729
730
181
    if (35 != len) {
731
138
        GPSD_LOG(LOG_WARN, &session->context->errout,
732
138
                 "Skytraq 0x8B: bad len %zu\n", len);
733
138
        return 0;
734
138
    }
735
736
43
    u[0] = getub(buf, 1);
737
43
    u[1] = getbeu32(buf, 2);
738
43
    u[2] = getbeu32(buf, 6);
739
43
    d[0] = getbed64((const char *)buf, 10);
740
43
    d[1] = getbed64((const char *)buf, 18);
741
43
    d[2] = getbef32((const char *)buf, 26);
742
43
    u[3] = getub(buf, 30);
743
43
    u[4] = getbeu32(buf, 31);
744
745
43
    GPSD_LOG(LOG_PROG, &session->context->errout,
746
43
             "Skytraq 0x8B: saved mode %u saved length %u stddev %u "
747
43
             "lat %.9f lon %.9f HAE %0.4f run mode %u survey len %u\n",
748
43
             u[0], u[1], u[2], d[0], d[1], d[2], u[3], u[4]);
749
750
43
    return 0;
751
181
}
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
362
{
761
362
    unsigned mode;
762
763
362
    if (2 != len) {
764
90
        GPSD_LOG(LOG_WARN, &session->context->errout,
765
90
                 "Skytraq 0x93: bad len %zu\n", len);
766
90
        return 0;
767
90
    }
768
769
272
    mode = getub(buf, 1);
770
771
272
    GPSD_LOG(LOG_PROG, &session->context->errout,
772
272
             "Skytraq 0x93: mode %u\n", mode);
773
272
    return 0;
774
362
}
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
131
{
792
131
    unsigned char  navmode; // Navigation fix mode (0: No fix, 1: 2D, 2: 3D, 3: 3D+DGNSS
793
131
    unsigned short week;    // GNSS week number
794
131
    double   ftow;       // Time of week
795
131
    timespec_t ts_tow;
796
131
    char ts_buf[TIMESPEC_LEN];
797
798
131
    int *mode = &session->newdata.mode;
799
131
    int *status = &session->newdata.status;
800
131
    gps_mask_t mask = 0;
801
802
131
    if (59 != len) {
803
123
        GPSD_LOG(LOG_INF, &session->context->errout, "Skytraq: "
804
123
            "Navigation Data Message has incorrect length %zu\n", len);
805
123
        return 0;
806
123
    }
807
808
8
    navmode = getub(buf, 1);
809
8
    switch (navmode) {
810
1
    case 1: // 2D fix
811
1
        *mode = MODE_2D;
812
1
        *status = STATUS_GPS;
813
1
        break;
814
4
    case 2: // 3D fix
815
4
        *mode = MODE_3D;
816
4
        *status = STATUS_GPS;
817
4
        break;
818
1
    case 3: // 3D DGPS fix
819
1
        *mode = MODE_3D;
820
1
        *status = STATUS_DGPS;
821
1
        break;
822
823
2
    default: // Includes SKY_MODE_NONE
824
2
        *mode = MODE_NO_FIX;
825
2
        *status = STATUS_UNK;
826
2
        break;
827
8
    }
828
8
    mask |= MODE_SET | STATUS_SET;
829
830
8
    session->gpsdata.satellites_used = getub(buf, 2);
831
832
8
    mask |= ONLINE_SET;
833
8
    week = getbeu16(buf,  3);
834
8
    ftow = getbeu32(buf,  5) / 100.0;
835
8
    DTOTS(&ts_tow, (int)ftow);
836
837
8
    session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
838
8
    mask |= TIME_SET;
839
840
8
    if (MODE_2D == *mode || MODE_3D == *mode) {
841
6
        session->newdata.latitude  = getbes32(buf,  9) / 1e7;
842
6
        session->newdata.longitude = getbes32(buf, 13) / 1e7;
843
6
        mask |= LATLON_SET | NTPTIME_IS;
844
845
6
        if (MODE_3D == *mode) {
846
5
            session->newdata.altHAE  = getbes32(buf, 17) / 100.0;
847
5
            session->newdata.altMSL  = getbes32(buf, 21) / 100.0;
848
849
5
            session->newdata.ecef.x  = getbes32(buf, 35) / 100.0;
850
5
            session->newdata.ecef.y  = getbes32(buf, 39) / 100.0;
851
5
            session->newdata.ecef.z  = getbes32(buf, 43) / 100.0;
852
5
            session->newdata.ecef.vx = getbes32(buf, 47) / 100.0;
853
5
            session->newdata.ecef.vy = getbes32(buf, 51) / 100.0;
854
5
            session->newdata.ecef.vz = getbes32(buf, 55) / 100.0;
855
5
            mask |= ECEF_SET | VECEF_SET | ALTITUDE_SET;
856
5
        }
857
6
    }
858
859
8
    session->gpsdata.dop.gdop = getbeu16(buf, 25) / 100.0;
860
8
    session->gpsdata.dop.pdop = getbeu16(buf, 27) / 100.0;
861
8
    session->gpsdata.dop.hdop = getbeu16(buf, 29) / 100.0;
862
8
    session->gpsdata.dop.vdop = getbeu16(buf, 31) / 100.0;
863
8
    session->gpsdata.dop.tdop = getbeu16(buf, 33) / 100.0;
864
8
    mask |= DOP_SET | CLEAR_IS | REPORT_IS;
865
8
    GPSD_LOG(LOG_DATA, &session->context->errout,
866
8
         "Skytraq: NAVDATA time=%s, lat=%.7f lon=%.7f altHAE=%.2f altMSL=%.2f mode=%d status=%d "
867
8
         "gdop: %.2f, hdop: %.2f, pdop: %.2f, tdop: %.2f, vdop: %.2f\n",
868
8
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
869
8
         session->newdata.latitude,
870
8
         session->newdata.longitude,
871
8
         session->newdata.altHAE,
872
8
         session->newdata.altMSL,
873
8
         session->newdata.mode,
874
8
         session->newdata.status,
875
8
         session->gpsdata.dop.gdop,
876
8
         session->gpsdata.dop.hdop,
877
8
         session->gpsdata.dop.pdop,
878
8
         session->gpsdata.dop.tdop,
879
8
         session->gpsdata.dop.vdop);
880
8
    return mask;
881
8
}
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.94k
{
891
1.94k
    unsigned datum;
892
893
1.94k
    if (3 != len) {
894
1.65k
        GPSD_LOG(LOG_WARN, &session->context->errout,
895
1.65k
                 "Skytraq 0xAE: bad len %zu\n", len);
896
1.65k
        return 0;
897
1.65k
    }
898
899
284
    datum  = getbeu16(buf, 1);
900
901
284
    GPSD_LOG(LOG_PROG, &session->context->errout,
902
284
             "Skytraq 0xAE: datum %u\n", datum);
903
284
    return 0;
904
1.94k
}
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
142
{
914
142
    unsigned mode, pdop, hdop, gdop;
915
916
142
    if (8 != len) {
917
141
        GPSD_LOG(LOG_WARN, &session->context->errout,
918
141
                 "Skytraq 0xAF: bad len %zu\n", len);
919
141
        return 0;
920
141
    }
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
142
}
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
167
{
941
167
    unsigned select, elevation, cnr;
942
943
167
    if (4 != len) {
944
166
        GPSD_LOG(LOG_WARN, &session->context->errout,
945
166
                 "Skytraq 0xB0: bad len %zu\n", len);
946
166
        return 0;
947
166
    }
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
167
}
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
133
{
967
133
    unsigned status, pspeed, pcnt, uspeed, ucnt, udist;
968
969
133
    if (12 != len) {
970
132
        GPSD_LOG(LOG_WARN, &session->context->errout,
971
132
                 "Skytraq 0xB4: bad len %zu\n", len);
972
132
        return 0;
973
132
    }
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
133
}
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
296
{
997
296
    unsigned mode;
998
999
296
    if (2 != len) {
1000
206
        GPSD_LOG(LOG_WARN, &session->context->errout,
1001
206
                 "Skytraq 0xB9: bad len %zu\n", len);
1002
206
        return 0;
1003
206
    }
1004
1005
90
    mode = getub(buf, 1);
1006
1007
90
    GPSD_LOG(LOG_PROG, &session->context->errout,
1008
90
             "Skytraq 0xB9: mode %u\n", mode);
1009
90
    return 0;
1010
296
}
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
68
{
1020
68
    int delay;
1021
1022
68
    if (5 != len) {
1023
67
        GPSD_LOG(LOG_WARN, &session->context->errout,
1024
67
                 "Skytraq 0xBB: bad len %zu\n", len);
1025
67
        return 0;
1026
67
    }
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
68
}
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
453
{
1043
453
    unsigned iod;   // Issue of data 0 - 255
1044
453
    unsigned wn;    // week number 0 - 65535
1045
453
    unsigned tow;   // receiver tow 0 - 604799999 in mS
1046
453
    unsigned mp;    // measurement period 1 - 1000 ms
1047
453
    char ts_buf[TIMESPEC_LEN];
1048
453
    timespec_t ts_tow;
1049
1050
453
    if (10 != len) {
1051
385
        return 0;
1052
385
    }
1053
1054
68
    iod = (unsigned)getub(buf, 1);
1055
68
    wn = getbeu16(buf, 2);
1056
68
    tow = getbeu32(buf, 4);
1057
68
    mp = getbeu16(buf, 8);
1058
68
    MSTOTS(&ts_tow, tow);
1059
1060
    // should this be newdata.skyview_time?
1061
68
    session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, wn, ts_tow);
1062
1063
68
    GPSD_LOG(LOG_DATA, &session->context->errout,
1064
68
             "Skytraq 0xDC: iod %u wn %u tow %u mp %u t%s\n",
1065
68
             iod, wn, tow, mp,
1066
68
             timespec_str(&session->gpsdata.skyview_time, ts_buf,
1067
68
                          sizeof(ts_buf)));
1068
68
    return 0;
1069
453
}
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
966
{
1078
966
    unsigned i;     // generic loop variable
1079
1080
    // buf[0] os the message ID
1081
    // Issue of data 0 - 255
1082
966
    unsigned iod = (unsigned)getub(buf, 1);
1083
    // number of measurements
1084
966
    unsigned nmeas = (unsigned)getub(buf, 2);
1085
1086
966
    GPSD_LOG(LOG_DATA, &session->context->errout,
1087
966
             "Skytraq 0xDD: iod %u, nmeas %u len %zd\n",
1088
966
             iod, nmeas, len);
1089
1090
966
    if (len < (3 + (nmeas * 23))) {
1091
192
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1092
192
                 "Skytraq 0xDD: bad len %zd, s/b %d\n",
1093
192
                 len, 3 + (nmeas & 23));
1094
192
        return 0;
1095
192
    }
1096
1097
    // check IOD?
1098
774
    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
774
    memset(session->gpsdata.raw.meas, 0, sizeof(session->gpsdata.raw.meas));
1103
1104
4.16k
    for (i = 0; i < nmeas; i++) {
1105
3.39k
        const char *obs_code;
1106
3.39k
        int off = 3 + (23 * i);
1107
1108
3.39k
        uint8_t PRN = getub(buf, off + 0);
1109
        // carrier-to-noise density ratio dB-Hz
1110
3.39k
        uint8_t cno = getub(buf, off + 1);
1111
        // pseudorange in meters
1112
3.39k
        double prMes = getbed64((const char *)buf, off + 2);
1113
        // carrier phase in cycles
1114
3.39k
        double cpMes = getbed64((const char *)buf, off + 10);
1115
        // doppler in Hz, positive towards sat
1116
3.39k
        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
3.39k
        uint8_t trkStat = getub(buf, off + 22);
1126
3.39k
        uint8_t gnssId = 0;
1127
        // svId should always be from 0 to 99, except SBAS.
1128
3.39k
        uint8_t svId = 0;
1129
3.39k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1130
1131
3.39k
        session->gpsdata.raw.meas[i].gnssid = gnssId;
1132
3.39k
        switch (gnssId) {
1133
1.95k
        case 0:       // GPS
1134
1.95k
            FALLTHROUGH
1135
1.95k
        case 5:       // QZSS
1136
1.95k
            FALLTHROUGH
1137
2.13k
        case 20:      // IRNSS, just guessing here
1138
2.13k
            obs_code = "L1C";       // u-blox calls this L1C/A ?
1139
2.13k
            break;
1140
280
        case 1:       // SBAS
1141
280
            if (100 < svId) {
1142
                // Should be always true, but pacify Coverity 498045
1143
280
                svId -= 100;            // adjust for RINEX 3 svid
1144
280
            }
1145
280
            obs_code = "L1C";       // u-blox calls this L1C/A
1146
280
            break;
1147
0
        case 2:       // GALILEO
1148
0
            obs_code = "L1B";       // u-blox calls this E1OS
1149
0
            break;
1150
339
        case 3:       // BeiDou
1151
339
            obs_code = "L2I";       // u-blox calls this B1I
1152
339
            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
639
        case 6:       // GLONASS
1159
639
            obs_code = "L1C";       // u-blox calls this L1OF
1160
639
            break;
1161
3.39k
        }
1162
3.39k
        (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code,
1163
3.39k
                      sizeof(session->gpsdata.raw.meas[i].obs_code));
1164
1165
3.39k
        session->gpsdata.raw.meas[i].svid = svId;
1166
3.39k
        session->gpsdata.raw.meas[i].snr = cno;
1167
3.39k
        session->gpsdata.raw.meas[i].satstat = trkStat;
1168
3.39k
        if (trkStat & 1) {
1169
            // prMes valid
1170
1.71k
            session->gpsdata.raw.meas[i].pseudorange = prMes;
1171
1.71k
        } else {
1172
1.67k
            session->gpsdata.raw.meas[i].pseudorange = NAN;
1173
1.67k
        }
1174
3.39k
        if (trkStat & 2) {
1175
            // doppler valid
1176
1.83k
            session->gpsdata.raw.meas[i].doppler = doMes;
1177
1.83k
        } else {
1178
1.56k
            session->gpsdata.raw.meas[i].doppler = NAN;
1179
1.56k
        }
1180
3.39k
        if (trkStat & 4) {
1181
            // cpMes valid
1182
1.50k
            session->gpsdata.raw.meas[i].carrierphase = cpMes;
1183
1.88k
        } else {
1184
1.88k
            session->gpsdata.raw.meas[i].carrierphase = NAN;
1185
1.88k
        }
1186
3.39k
        session->gpsdata.raw.meas[i].codephase = NAN;
1187
3.39k
        session->gpsdata.raw.meas[i].deltarange = NAN;
1188
        // skytraq does not report locktime, so assume max
1189
3.39k
        session->gpsdata.raw.meas[i].locktime = LOCKMAX;
1190
3.39k
        if (trkStat & 8) {
1191
            // possible slip
1192
1.48k
            session->gpsdata.raw.meas[i].lli = 2;
1193
1.48k
        }
1194
3.39k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1195
3.39k
                 "PRN %u (%u:%u) prMes %f cpMes %f doMes %f\n"
1196
3.39k
                 "cno %u  rtkStat %u\n", PRN,
1197
3.39k
                 gnssId, svId, prMes, cpMes, doMes, cno, trkStat);
1198
1199
3.39k
    }
1200
1201
    // return RAW_IS;  // WIP
1202
774
    return 0;
1203
774
}
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.47k
{
1213
1.47k
    int st, nsv;
1214
1.47k
    unsigned i;
1215
1.47k
    unsigned iod;   // Issue of data 0 - 255
1216
1.47k
    unsigned nsvs;  // number of SVs in this packet
1217
1218
1.47k
    if (3 > len) {
1219
341
        GPSD_LOG(LOG_WARN, &session->context->errout,
1220
341
                 "Skytraq 0xDE: bad len %zu\n", len);
1221
341
        return 0;
1222
341
    }
1223
1.13k
    iod = (unsigned)getub(buf, 1);
1224
1.13k
    nsvs = (unsigned)getub(buf, 2);
1225
    // too many sats?
1226
1.13k
    if (SKY_CHANNELS < nsvs) {
1227
70
        return 0;
1228
70
    }
1229
1230
1.06k
    if (len < (3 + (nsvs * 10))) {
1231
115
        GPSD_LOG(LOG_WARN, &session->context->errout,
1232
115
                 "Skytraq 0xDE: bad len %zu\n", len);
1233
115
        return 0;
1234
115
    }
1235
948
    gpsd_zero_satellites(&session->gpsdata);
1236
4.12k
    for (i = st = nsv =  0; i < nsvs; i++) {
1237
3.17k
        int off = 3 + (10 * i);  // offset into buffer of start of this sat
1238
3.17k
        bool good;               // do we have a good record ?
1239
3.17k
        unsigned short sv_stat;
1240
3.17k
        unsigned short chan_stat;
1241
3.17k
        unsigned short ura;
1242
3.17k
        short PRN;
1243
3.17k
        uint8_t gnssId = 0;
1244
3.17k
        uint8_t svId = 0;
1245
1246
3.17k
        PRN = (short)getub(buf, off + 1);
1247
        // fit into gnssid:svid
1248
3.17k
        if (0 == PRN) {
1249
            // skip 0 PRN
1250
305
            continue;
1251
305
        }
1252
2.86k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1253
1254
2.86k
        session->gpsdata.skyview[st].gnssid = gnssId;
1255
2.86k
        session->gpsdata.skyview[st].svid = svId;
1256
2.86k
        session->gpsdata.skyview[st].PRN = PRN;
1257
1258
2.86k
        sv_stat = (unsigned short)getub(buf, off + 2);
1259
2.86k
        ura = (unsigned short)getub(buf, off + 3);
1260
2.86k
        session->gpsdata.skyview[st].ss = (double)getub(buf, off + 4);
1261
2.86k
        session->gpsdata.skyview[st].elevation =
1262
2.86k
            (double)getbes16(buf, off + 5);
1263
2.86k
        session->gpsdata.skyview[st].azimuth =
1264
2.86k
            (double)getbes16(buf, off + 7);
1265
2.86k
        chan_stat = (unsigned short)getub(buf, off + 9);
1266
1267
2.86k
        session->gpsdata.skyview[st].used = (bool)(chan_stat & 0x30);
1268
2.86k
        good = session->gpsdata.skyview[st].PRN != 0 &&
1269
2.86k
            session->gpsdata.skyview[st].azimuth != 0 &&
1270
2.59k
            session->gpsdata.skyview[st].elevation != 0;
1271
1272
2.86k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1273
2.86k
                 "Skytraq PRN=%2d El=%4.0f Az=%5.0f ss=%3.2f stat=%02x,%02x "
1274
2.86k
                 "ura=%d %c\n",
1275
2.86k
                 session->gpsdata.skyview[st].PRN,
1276
2.86k
                 session->gpsdata.skyview[st].elevation,
1277
2.86k
                 session->gpsdata.skyview[st].azimuth,
1278
2.86k
                 session->gpsdata.skyview[st].ss,
1279
2.86k
                 chan_stat, sv_stat, ura,
1280
2.86k
                 good ? '*' : ' ');
1281
1282
2.86k
        if (good) {
1283
2.42k
            st += 1;
1284
2.42k
            if (session->gpsdata.skyview[st].used) {
1285
18
                nsv++;
1286
18
            }
1287
2.42k
        }
1288
2.86k
    }
1289
1290
948
    session->gpsdata.satellites_visible = st;
1291
948
    if (MAXCHANNELS < session->gpsdata.satellites_visible) {
1292
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
1293
3
                "Skytraq 0xDE: too many satellites %d\n",
1294
3
                 session->gpsdata.satellites_visible);
1295
3
        session->gpsdata.satellites_visible = MAXCHANNELS;
1296
3
    }
1297
948
    session->gpsdata.satellites_used = nsv;
1298
1299
948
    GPSD_LOG(LOG_DATA, &session->context->errout,
1300
948
             "Skytraq 0xDE: nsvs=%u visible=%u iod=%u\n", nsvs,
1301
948
             session->gpsdata.satellites_visible, iod);
1302
948
    return SATELLITE_SET | USED_IS;
1303
1.06k
}
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
76
{
1313
76
    unsigned short navstat;
1314
76
    unsigned iod;           // Issue of data 0 - 255
1315
76
    unsigned wn;            // week number 0 - 65535
1316
76
    double f_tow;           // receiver tow Sec
1317
76
    double clock_bias;
1318
76
    double clock_drift;
1319
76
    gps_mask_t mask = 0;
1320
76
    timespec_t ts_tow;
1321
76
    char ts_buf[TIMESPEC_LEN];
1322
1323
76
    if (81 != len) {
1324
74
        return 0;
1325
74
    }
1326
1327
2
    iod = (unsigned)getub(buf, 1);
1328
1329
    // fix status is byte 2
1330
2
    navstat = (unsigned short)getub(buf, 2);
1331
2
    session->newdata.status = STATUS_UNK;
1332
2
    session->newdata.mode = MODE_NO_FIX;
1333
2
    switch (navstat) {
1334
0
    case 1:
1335
        // fix prediction, ignore
1336
0
        break;
1337
1
    case 2:
1338
1
        session->newdata.status = STATUS_GPS;
1339
1
        session->newdata.mode = MODE_2D;
1340
1
        break;
1341
0
    case 3:
1342
0
        session->newdata.status = STATUS_GPS;
1343
0
        session->newdata.mode = MODE_3D;
1344
0
        break;
1345
0
    case 4:
1346
0
        session->newdata.status = STATUS_DGPS;
1347
0
        session->newdata.mode = MODE_3D;
1348
0
        break;
1349
1
    default:
1350
1
        break;
1351
2
    }
1352
1353
2
    wn = getbeu16(buf, 3);
1354
2
    f_tow = getbed64((const char *)buf, 5);
1355
2
    DTOTS(&ts_tow, f_tow);
1356
1357
    // position/velocity is bytes 13-48, meters and m/s
1358
2
    session->newdata.ecef.x = (double)getbed64((const char *)buf, 13),
1359
2
    session->newdata.ecef.y = (double)getbed64((const char *)buf, 21),
1360
2
    session->newdata.ecef.z = (double)getbed64((const char *)buf, 29),
1361
2
    session->newdata.ecef.vx = (double)getbef32((const char *)buf, 37),
1362
2
    session->newdata.ecef.vy = (double)getbef32((const char *)buf, 41),
1363
2
    session->newdata.ecef.vz = (double)getbef32((const char *)buf, 45);
1364
2
    mask |= ECEF_SET | VECEF_SET;
1365
1366
2
    clock_bias = getbed64((const char *)buf, 49);
1367
2
    clock_drift = getbes32(buf, 57);
1368
1369
2
    session->gpsdata.dop.gdop = getbef32((const char *)buf, 61);
1370
2
    session->gpsdata.dop.pdop = getbef32((const char *)buf, 65);
1371
2
    session->gpsdata.dop.hdop = getbef32((const char *)buf, 69);
1372
2
    session->gpsdata.dop.vdop = getbef32((const char *)buf, 73);
1373
2
    session->gpsdata.dop.tdop = getbef32((const char *)buf, 77);
1374
2
    mask |= DOP_SET;
1375
1376
2
    session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow );
1377
1378
2
    GPSD_LOG(LOG_DATA, &session->context->errout,
1379
2
             "Skytraq 0xDF: iod=%u, stat=%u, wn=%u, tow=%f, t=%s "
1380
2
             "cb: %f, cd: %f "
1381
2
             "gdop: %.2f, pdop: %.2f, hdop: %.2f, vdop: %.2f, tdop: %.2f\n",
1382
2
             iod, navstat, wn, f_tow,
1383
2
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1384
2
             clock_bias, clock_drift,
1385
2
             session->gpsdata.dop.gdop,
1386
2
             session->gpsdata.dop.pdop,
1387
2
             session->gpsdata.dop.hdop,
1388
2
             session->gpsdata.dop.vdop,
1389
2
             session->gpsdata.dop.tdop);
1390
1391
2
    mask |= TIME_SET | STATUS_SET | MODE_SET | CLEAR_IS | REPORT_IS;
1392
2
    return mask;
1393
2
}
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
322
{
1404
322
    int i;
1405
322
    unsigned prn;   // GPS sat PRN
1406
322
    unsigned subf;  // subframe 1-5
1407
    // the words are preprocessed, not raw, just the 24bits of data
1408
322
    uint32_t words[10];  // subframe 1-5
1409
1410
322
    if (33 != len) {
1411
139
        return 0;
1412
139
    }
1413
1414
183
    prn = (unsigned)getub(buf, 1);
1415
183
    subf = (unsigned)getub(buf, 2);
1416
2.01k
    for (i = 0; i < 10; i++) {
1417
1.83k
        words[i] = (uint32_t)getbeu24(buf, 3 + (i * 3));
1418
1.83k
    }
1419
1420
183
    GPSD_LOG(LOG_DATA, &session->context->errout,
1421
183
             "Skytraq 0xE0: prn=%u, subf=%u\n",
1422
183
             prn, subf);
1423
1424
    // could be SBAS?
1425
183
    return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words);
1426
322
}
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
174
{
1440
174
    int i;
1441
174
    unsigned prn;   // BeidouPS sat PRN 206-214
1442
174
    unsigned subf;  // subframe 1-5
1443
    // the words are preprocessed, not raw, just the 28 bytes of data
1444
174
    uint8_t bytes[28];  // raw data
1445
1446
174
    if (31 != len) {
1447
173
        return 0;
1448
173
    }
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
174
}
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
404
{
1478
404
    int i;
1479
404
    unsigned prn;   // BeidouPS sat PRN 201-205
1480
404
    unsigned subf;  // subframe 1-5
1481
    // the words are preprocessed, not raw, just the 28 bytes of data
1482
404
    uint8_t bytes[28];  // raw data
1483
1484
404
    if (31 != len) {
1485
403
        return 0;
1486
403
    }
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
404
}
1503
1504
1505
static gps_mask_t sky_parse(struct gps_device_t * session,
1506
                            unsigned char *buf, size_t len)
1507
14.7k
{
1508
14.7k
    gps_mask_t mask = 0;
1509
1510
14.7k
    if (0 == len) {
1511
0
        return mask;
1512
0
    }
1513
1514
14.7k
    buf += 4;   // skip the leaders and length
1515
14.7k
    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
14.7k
    switch (buf[0]) {
1524
640
    case 0x62:
1525
640
        mask = sky_msg_62(session, buf, len);
1526
640
        break;
1527
503
    case 0x63:
1528
503
        mask = sky_msg_63(session, buf, len);
1529
503
        break;
1530
1.02k
    case 0x64:
1531
1.02k
        mask = sky_msg_64(session, buf, len);
1532
1.02k
        break;
1533
505
    case 0x65:
1534
505
        mask = sky_msg_65(session, buf, len);
1535
505
        break;
1536
744
    case 0x6A:
1537
744
        mask = sky_msg_6A(session, buf, len);
1538
744
        break;
1539
253
    case 0x7A:
1540
253
        mask = sky_msg_7A(session, buf, len);
1541
253
        break;
1542
254
    case 0x80:
1543
        // 128
1544
254
        mask = sky_msg_80(session, buf, len);
1545
254
        break;
1546
94
    case 0x81:
1547
        // Software CRC
1548
94
        mask = sky_msg_81(session, buf, len);
1549
94
        break;
1550
691
    case 0x83:
1551
        // 131 - ACK
1552
691
        if (2 == len) {
1553
93
            GPSD_LOG(LOG_PROG, &session->context->errout,
1554
93
                     "Skytraq 0x83: ACK MID %#02x\n", buf[1]);
1555
598
        } else if (3 == len) {
1556
120
            GPSD_LOG(LOG_PROG, &session->context->errout,
1557
120
                     "Skytraq 0x83: ACK MID %#02x/%02x\n", buf[1], buf[2]);
1558
478
        } else if (4 <= len) {
1559
352
            GPSD_LOG(LOG_PROG, &session->context->errout,
1560
352
                     "Skytraq 0x83: ACK MID %#02x/%02x/%02x\n",
1561
352
                     buf[1], buf[2], buf[3]);
1562
352
        } else {
1563
126
            GPSD_LOG(LOG_PROG, &session->context->errout,
1564
126
                     "Skytraq 0x83: ACK\n");
1565
126
        }
1566
691
        break;
1567
627
    case 0x84:
1568
        // 132 - NACK
1569
627
        if (2 == len) {
1570
134
            GPSD_LOG(LOG_INF, &session->context->errout,
1571
134
                     "Skytraq 0x84: NACK MID %#02x\n", buf[1]);
1572
493
        } else if (3 == len) {
1573
108
            GPSD_LOG(LOG_INF, &session->context->errout,
1574
108
                     "Skytraq 0x84: NACK MID %#02x/%02x\n", buf[1], buf[2]);
1575
385
        } else if (4 <= len) {
1576
119
            GPSD_LOG(LOG_INF, &session->context->errout,
1577
119
                     "Skytraq 0x84: NACK MID %#02x/%02x/x%02x\n",
1578
119
                     buf[1], buf[2], buf[3]);
1579
266
        } else {
1580
266
            GPSD_LOG(LOG_INF, &session->context->errout,
1581
266
                     "Skytraq 0x84: NACK\n");
1582
266
        }
1583
627
        break;
1584
146
    case 0x86:
1585
        // 134 Position Update Rate
1586
146
        mask = sky_msg_86(session, buf, len);
1587
146
        break;
1588
168
    case 0x89:
1589
168
        mask = sky_msg_89(session, buf, len);
1590
168
        break;
1591
645
    case 0x8A:
1592
645
        mask = sky_msg_8A(session, buf, len);
1593
645
        break;
1594
181
    case 0x8B:
1595
181
        mask = sky_msg_8B(session, buf, len);
1596
181
        break;
1597
362
    case 0x93:
1598
        // NMEA TALKER id
1599
362
        mask = sky_msg_93(session, buf, len);
1600
362
        break;
1601
1602
131
    case 0xA8:
1603
        // Navigation Data Message
1604
131
        mask = sky_msg_A8(session, buf, len);
1605
131
        break;
1606
1607
1.94k
    case 0xae:
1608
        // GNSS Datum
1609
1.94k
        mask = sky_msg_AE(session, buf, len);
1610
1.94k
        break;
1611
1612
142
    case 0xaf:
1613
        // DOP Mask
1614
142
        mask = sky_msg_AF(session, buf, len);
1615
142
        break;
1616
1617
167
    case 0xb0:
1618
        // Elevation and CNR mask
1619
167
        mask = sky_msg_B0(session, buf, len);
1620
167
        break;
1621
1622
133
    case 0xb4:
1623
        // Position Pinning Status
1624
133
        mask = sky_msg_B4(session, buf, len);
1625
133
        break;
1626
1627
296
    case 0xb9:
1628
296
        mask = sky_msg_B9(session, buf, len);
1629
296
        break;
1630
1631
68
    case 0xbb:
1632
68
        mask = sky_msg_BB(session, buf, len);
1633
68
        break;
1634
1635
453
    case 0xDC:
1636
        // 220
1637
453
        mask = sky_msg_DC(session, buf, len);
1638
453
        break;
1639
1640
1.23k
    case 0xDD:
1641
        // 221
1642
1.23k
        if (3 <= len) {
1643
966
            mask = sky_msg_DD(session, buf, len);
1644
966
        }
1645
1.23k
        break;
1646
1647
1.47k
    case 0xDE:
1648
        // 222
1649
1.47k
        mask = sky_msg_DE(session, buf, len);
1650
1.47k
        break;
1651
1652
76
    case 0xDF:
1653
        // 223 - Nave status (PVT)
1654
76
        mask = sky_msg_DF(session, buf, len);
1655
76
        break;
1656
1657
322
    case 0xE0:
1658
        // 224
1659
322
        mask = sky_msg_E0(session, buf, len);
1660
322
        break;
1661
1662
174
    case 0xE2:
1663
        // 226 - Beidou2 D1 Subframe data
1664
174
        mask = sky_msg_E2(session, buf, len);
1665
174
        break;
1666
1667
404
    case 0xE3:
1668
        // 227 - Beidou2 D2 Subframe data
1669
404
        mask = sky_msg_E3(session, buf, len);
1670
404
        break;
1671
1672
133
    case 0x67:   // sub-id messages
1673
133
        FALLTHROUGH
1674
280
    case 0x6F:   // sub-id messages
1675
280
        GPSD_LOG(LOG_PROG, &session->context->errout,
1676
280
                 "Skytraq Unknown MID x%02x SID x%02x length %zd\n",
1677
280
                 buf[0], buf[1], len);
1678
280
        break;
1679
650
    default:
1680
650
        GPSD_LOG(LOG_PROG, &session->context->errout,
1681
650
                 "Skytraq Unknown MID %#02x length %zd\n",
1682
650
                 buf[0], len);
1683
650
        break;
1684
14.7k
    }
1685
14.7k
    return mask;
1686
14.7k
}
1687
1688
static gps_mask_t skybin_parse_input(struct gps_device_t *session)
1689
17.7k
{
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
17.7k
    if (UINT_MAX != session->cfg_stage) {
1697
9.93k
        session->cfg_step++;
1698
9.93k
    }
1699
1700
17.7k
    if (UINT_MAX != session->cfg_stage &&
1701
9.93k
        3 <= session->cfg_step) {
1702
        // more init to do
1703
1704
3.32k
        session->cfg_stage++;
1705
3.32k
        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
3.32k
        switch (session->cfg_stage) {
1713
168
        case 1:
1714
            // Send MID 0x3, to get back MID 0x81 Software CRC
1715
168
            (void)sky_write(session, "\xA0\xA1\x00\x02\x03\x00\x03\x0d\x0a",
1716
168
                            9);
1717
168
            break;
1718
164
        case 2:
1719
            // Send MID 0x10, to get back MID 0x86 (Position Update Rate)
1720
164
            (void)sky_write(session, "\xA0\xA1\x00\x01\x10\x10\x0d\x0a", 8);
1721
164
            break;
1722
174
        case 3:
1723
            // Send MID 0x15, to get back MID 0xB9 (Power Mode Status)
1724
174
            (void)sky_write(session, "\xA0\xA1\x00\x01\x15\x15\x0d\x0a", 8);
1725
174
            break;
1726
173
        case 4:
1727
            // Send MID 0x1f, to get back MID 0x89 Measurement data statuS
1728
173
            (void)sky_write(session, "\xA0\xA1\x00\x01\x1f\x1f\x0d\x0a", 8);
1729
173
            break;
1730
163
        case 5:
1731
            // Send MID 0x21, to get back MID 0x8a  RTCM Data output status
1732
163
            (void)sky_write(session, "\xA0\xA1\x00\x01\x21\x21\x0d\x0a", 8);
1733
163
            break;
1734
153
        case 6:
1735
            // Send MID 0x23, to get back MID 0x8B (Base Position)
1736
153
            (void)sky_write(session, "\xA0\xA1\x00\x01\x23\x23\x0d\x0a", 8);
1737
153
            break;
1738
138
        case 7:
1739
            // Send MID 0x2d, to get back MID 0xAE (GNSS Datum)
1740
138
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2d\x2d\x0d\x0a", 8);
1741
138
            break;
1742
145
        case 8:
1743
            // Send MID 0x2E, to get back MID 0xAF (DOP Mask)
1744
145
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2e\x2e\x0d\x0a", 8);
1745
145
            break;
1746
134
        case 9:
1747
            // Send MID 0x2F, to get back MID 0x80 Elevation and SNR mask
1748
134
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2f\x2f\x0d\x0a", 8);
1749
134
            break;
1750
133
        case 10:
1751
            // Send MID 0x3a, to get back MID 0xb4 Position Pinning
1752
133
            (void)sky_write(session, "\xA0\xA1\x00\x01\x3a\x3a\x0d\x0a", 8);
1753
133
            break;
1754
122
        case 11:
1755
            // Send MID 0x44, to get back MID 0xc2 1PPS timing
1756
            // Timing mode versions only
1757
122
            (void)sky_write(session, "\xA0\xA1\x00\x01\x44\x44\x0d\x0a", 8);
1758
122
            break;
1759
119
        case 12:
1760
            // Send MID 0x46, to get back MID 0xbb 1PPS delay
1761
119
            (void)sky_write(session, "\xA0\xA1\x00\x01\x46\x46\x0d\x0a", 8);
1762
119
            break;
1763
116
        case 13:
1764
            // Send MID 0x4f, to get back MID 0x93 NMEA talker ID
1765
116
            (void)sky_write(session, "\xA0\xA1\x00\x01\x4f\x4f\x0d\x0a", 8);
1766
116
            break;
1767
106
        case 14:
1768
            // Send MID 0x56, to get back MID 0xc3 1PPS Output Mode
1769
            // Timing mode versions only
1770
106
            (void)sky_write(session, "\xA0\xA1\x00\x01\x56\x56\x0d\x0a", 8);
1771
106
            break;
1772
96
        case 15:
1773
            // Send MID 0x62/02, to get back MID 0x62/80 SBAS status
1774
96
            (void)sky_write(session,
1775
96
                            "\xA0\xA1\x00\x02\x62\x02\x60\x0d\x0a", 9);
1776
96
            break;
1777
92
        case 16:
1778
            // Send MID 0x62/04, to get back MID 0x62/81 QZSS status
1779
92
            (void)sky_write(session,
1780
92
                            "\xA0\xA1\x00\x02\x62\x04\x66\x0d\x0a", 9);
1781
92
            break;
1782
88
        case 17:
1783
            // Send MID 0x62/06, to get back MID 0x62/82 SBAS Advanced status
1784
88
            (void)sky_write(session,
1785
88
                            "\xA0\xA1\x00\x02\x62\x06\x64\x0d\x0a", 9);
1786
88
            break;
1787
82
        case 18:
1788
            // Send MID 0x63/02, to get back MID 0x62/80 SAEE Status
1789
            // not on PX1172RH_DS
1790
82
            (void)sky_write(session,
1791
82
                            "\xA0\xA1\x00\x02\x63\x02\x61\x0d\x0a", 9);
1792
82
            break;
1793
75
        case 19:
1794
            // Send MID 0x64/01, to get back MID 0x64/80
1795
75
            (void)sky_write(session,
1796
75
                            "\xA0\xA1\x00\x02\x64\x01\x65\x0d\x0a", 9);
1797
75
            break;
1798
68
        case 20:
1799
            // Send MID 0x64/03, to get back MID 0x64/81
1800
68
            (void)sky_write(session,
1801
68
                            "\xA0\xA1\x00\x02\x64\x03\x67\x0d\x0a", 9);
1802
68
            break;
1803
65
        case 21:
1804
            // Send MID 0x64/07, to get back MID 0x64/83
1805
65
            (void)sky_write(session,
1806
65
                            "\xA0\xA1\x00\x02\x64\x07\x63\x0d\x0a", 9);
1807
65
            break;
1808
59
        case 22:
1809
            // Send MID 0x64/0b, to get back MID 0x64/85
1810
59
            (void)sky_write(session,
1811
59
                            "\xA0\xA1\x00\x02\x64\x0b\x6f\x0d\x0a", 9);
1812
59
            break;
1813
53
        case 23:
1814
            // Send MID 0x64/12, to get back MID 0x64/88
1815
53
            (void)sky_write(session,
1816
53
                            "\xA0\xA1\x00\x02\x64\x12\x76\x0d\x0a", 9);
1817
53
            break;
1818
53
        case 24:
1819
            // Send MID 0x64/16, to get back MID 0x64/8a
1820
53
            (void)sky_write(session,
1821
53
                            "\xA0\xA1\x00\x02\x64\x16\x72\x0d\x0a", 9);
1822
53
            break;
1823
51
        case 25:
1824
            // Send MID 0x64/18, to get back MID 0x64/8b
1825
51
            (void)sky_write(session,
1826
51
                            "\xA0\xA1\x00\x02\x64\x18\x7c\x0d\x0a", 9);
1827
51
            break;
1828
50
        case 26:
1829
            // Send MID 0x64/1a, to get back MID 0x64/8c
1830
50
            (void)sky_write(session,
1831
50
                            "\xA0\xA1\x00\x02\x64\x1a\x7e\x0d\x0a", 9);
1832
50
            break;
1833
46
        case 27:
1834
            // Send MID 0x64/20, to get back MID 0x64/8e
1835
46
            (void)sky_write(session,
1836
46
                            "\xA0\xA1\x00\x02\x64\x20\x44\x0d\x0a", 9);
1837
46
            break;
1838
43
        case 28:
1839
            // Send MID 0x64/22, to get back MID 0x64/8f
1840
            // not on PX1172RH_DS
1841
43
            (void)sky_write(session,
1842
43
                            "\xA0\xA1\x00\x02\x64\x22\x46\x0d\x0a", 9);
1843
43
            break;
1844
38
        case 29:
1845
            // Send MID 0x64/28, to get back MID 0x64/92
1846
38
            (void)sky_write(session,
1847
38
                            "\xA0\xA1\x00\x02\x64\x28\x4c\x0d\x0a", 9);
1848
38
            break;
1849
36
        case 30:
1850
            // Send MID 0x64/30, to get back MID 0x64/98
1851
            // not on PX1172RH_DS
1852
36
            (void)sky_write(session,
1853
36
                            "\xA0\xA1\x00\x02\x64\x30\x54\x0d\x0a", 9);
1854
36
            break;
1855
35
        case 31:
1856
            // Send MID 0x64/31, to get back MID 0x64/
1857
            // not on PX1172RH_DS
1858
35
            (void)sky_write(session,
1859
35
                            "\xA0\xA1\x00\x02\x64\x31\x55\x0d\x0a", 9);
1860
35
            break;
1861
31
        case 32:
1862
            // Send MID 0x64/7d, to get back MID 0x64/fe
1863
31
            (void)sky_write(session,
1864
31
                            "\xA0\xA1\x00\x02\x64\x7d\x19\x0d\x0a", 9);
1865
31
            break;
1866
29
        case 33:
1867
            // Send MID 0x64/35, to get back MID 0x64/99
1868
            // not on PX1172RH_DS
1869
29
            (void)sky_write(session,
1870
29
                            "\xA0\xA1\x00\x03\x64\x35\x01\x50\x0d\x0a", 10);
1871
29
            break;
1872
26
        case 34:
1873
            // Send MID 0x64/36, to get back MID 0x64/9a
1874
            // not on PX1172RH_DS
1875
26
            (void)sky_write(session,
1876
26
                            "\xA0\xA1\x00\x02\x64\x36\x52\x0d\x0a", 9);
1877
26
            break;
1878
24
        case 35:
1879
            // Send MID 0x64/3c, to get back MID 0x64/99
1880
            // not on PX1172RH_DS
1881
24
            (void)sky_write(session,
1882
24
                            "\xA0\xA1\x00\x04\x64\x3c\x47\x47\x19\x0d\x0a",
1883
24
                            11);
1884
24
            break;
1885
22
        case 36:
1886
            // Send MID 0x65/02, to get back MID 0x64/80
1887
22
            (void)sky_write(session,
1888
22
                            "\xA0\xA1\x00\x02\x65\x02\x67\x0d\x0a", 9);
1889
22
            break;
1890
22
        case 37:
1891
            // Send MID 0x65/04, to get back MID 0x64/8f
1892
22
            (void)sky_write(session,
1893
22
                            "\xA0\xA1\x00\x02\x65\x04\x61\x0d\x0a", 9);
1894
22
            break;
1895
19
        case 38:
1896
            // Send MID 0x6a/02, to get back MID 0x6a/83
1897
19
            (void)sky_write(session,
1898
19
                            "\xA0\xA1\x00\x02\x6a\x02\x68\x0d\x0a", 9);
1899
19
            break;
1900
16
        case 39:
1901
            // Send MID 0x6a/07, to get back MID 0x6a/83
1902
16
            (void)sky_write(session,
1903
16
                            "\xA0\xA1\x00\x02\x6a\x07\x6d\x0d\x0a", 9);
1904
16
            break;
1905
16
        case 40:
1906
            // Send MID 0x6a/0d, to get back MID 0x6a/85
1907
16
            (void)sky_write(session,
1908
16
                            "\xA0\xA1\x00\x02\x6a\x0d\x67\x0d\x0a", 9);
1909
16
            break;
1910
15
        case 41:
1911
            // Send MID 0x6a/14, to get back MID 0x6a/86
1912
15
            (void)sky_write(session,
1913
15
                            "\xA0\xA1\x00\x02\x6a\x14\xfd\x0d\x0a", 9);
1914
15
            break;
1915
11
        case 42:
1916
            // Send MID 0x6a/16, to get back MID 0x6a/89
1917
            // not on PX1172RH_DS ?
1918
11
            (void)sky_write(session,
1919
11
                            "\xA0\xA1\x00\x02\x6a\x16\x7c\x0d\x0a", 9);
1920
11
            break;
1921
11
        case 43:
1922
            // Send MID 0x7a/0e/01, to get back MID 0x7a/0e/80
1923
            // not on PX1172RH_DS ?
1924
11
            (void)sky_write(session,
1925
11
                            "\xA0\xA1\x00\x03\x7a\x0e\x01\x75\x0d\x0a", 10);
1926
11
            break;
1927
11
        case 44:
1928
            // Send MID 0x7a/0e/02, to get back MID 0x7a/0e/81
1929
            // not on PX1172RH_DS ?
1930
11
            (void)sky_write(session,
1931
11
                            "\xA0\xA1\x00\x03\x7a\x0e\x02\x76\x0d\x0a", 10);
1932
11
            break;
1933
11
        case 45:
1934
            // Send MID 0x7a/0e/03, to get back MID 0x7a/0e/82
1935
            // not on PX1172RH_DS ?
1936
11
            (void)sky_write(session,
1937
11
                            "\xA0\xA1\x00\x03\x7a\x0e\x03\x77\x0d\x0a", 10);
1938
11
            break;
1939
9
        case 46:
1940
            // Send MID 0x7a/0e/05, to get back MID 0x7a/0e/83
1941
            // not on PX1172RH_DS ?
1942
9
            (void)sky_write(session,
1943
9
                            "\xA0\xA1\x00\x03\x7a\x0e\x05\x71\x0d\x0a", 10);
1944
9
            break;
1945
9
        default:
1946
            // Done
1947
9
            session->cfg_stage = UINT_MAX;
1948
9
            break;
1949
3.32k
        }
1950
3.32k
    }
1951
1952
17.7k
    if (SKY_PACKET == session->lexer.type) {
1953
14.7k
        return  sky_parse(session, session->lexer.outbuffer,
1954
14.7k
                        session->lexer.outbuflen);
1955
14.7k
    }
1956
2.98k
    if (NMEA_PACKET == session->lexer.type) {
1957
2.98k
        return nmea_parse((char *)session->lexer.outbuffer, session);
1958
2.98k
    }
1959
    // should not get here...
1960
1961
0
    return 0;
1962
2.98k
}
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