Coverage Report

Created: 2026-09-14 06:39

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