Coverage Report

Created: 2026-06-13 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_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
2.38k
#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.54k
{
58
1.54k
    uint8_t chk;
59
1.54k
    uint16_t len;
60
1.54k
    ssize_t i;
61
1.54k
    bool ok;
62
1.54k
    unsigned type = (unsigned)msg[4];
63
1.54k
    uint8_t buf[BUFSIZ];
64
1.54k
    uint8_t outbuf[BUFSIZ];
65
66
    // do not write if -b (readonly) option set
67
    // "passive" handled earlier
68
1.54k
    if (session->context->readonly) {
69
582
        return data_len;
70
582
    }
71
72
965
    if (sizeof(buf) <= data_len) {
73
        // uh, oh;
74
0
        return -1;
75
0
    }
76
    // make a copy, so we can edit it
77
965
    memcpy(buf, msg, data_len);
78
79
    // max length is undocumented, largest I could find is 261
80
965
    len = (buf[2] << 8) | buf[3];
81
    // limit to 512 to pacify coverity
82
965
    if (512 < len) {
83
0
        len = 512;
84
0
    }
85
965
    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
965
    chk = 0;
97
    // coverity_submit[tainted_data]
98
2.31k
    for (i = 0; i < len; i++) {
99
1.35k
        chk ^= buf[4 + i];
100
1.35k
    }
101
102
    // enter checksum after payload
103
965
    buf[len + 4] = chk;
104
965
    len += 7;
105
106
965
    GPSD_LOG(LOG_IO, &session->context->errout,
107
965
             "Skytraq: Writing control MID %02x: %s\n", type,
108
965
             gps_hexdump((char *)outbuf, sizeof(outbuf), buf, len));
109
965
    ok = gpsd_write(session, (const char *)buf, len) == len;
110
111
965
    return ok;
112
965
}
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
5.05k
{
150
    // fit into gnssid:svid
151
5.05k
    if (0 == PRN) {
152
        // skip 0 PRN
153
422
        *gnssId = 0;
154
422
        *svId = 0;
155
4.63k
    } else if ((1 <= PRN) &&
156
4.63k
               (32 >= PRN)) {
157
        // GPS
158
865
        *gnssId = 0;
159
865
        *svId = PRN;
160
3.76k
    } else if ((65 <= PRN) &&
161
3.20k
               (96 >= PRN)) {
162
        // GLONASS
163
697
        *gnssId = 6;
164
697
        *svId = PRN - 64;
165
3.06k
    } else if ((120 <= PRN) &&
166
2.23k
               (158 >= PRN)) {
167
        // SBAS
168
508
        *gnssId = 1;
169
508
        *svId = PRN;
170
2.56k
    } else if ((201 <= PRN) &&
171
1.37k
               (239 >= PRN)) {
172
        // BeiDou
173
453
        *gnssId = 3;
174
453
        *svId = PRN - 200;
175
2.10k
    } else if ((240 <= PRN) &&
176
920
               (254 >= PRN)) {
177
        // IRNSS
178
369
        *gnssId = 20;
179
369
        *svId = PRN - 240;
180
1.73k
    } else {
181
        // huh?
182
1.73k
        *gnssId = 0;
183
1.73k
        *svId = 0;
184
1.73k
    }
185
5.05k
    return;
186
5.05k
}
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
582
{
196
582
    unsigned sid;
197
582
    unsigned u[23];
198
582
    int i;
199
200
582
    if (3 > len) {
201
80
        GPSD_LOG(LOG_WARN, &session->context->errout,
202
80
                 "Skytraq 0x62: bad len %zu\n", len);
203
80
        return 0;
204
80
    }
205
206
502
    sid = getub(buf, 1);
207
502
    switch (sid) {
208
209
    case  0x80:
209
        // SBAS status
210
209
        if (8 < len) {
211
67
            GPSD_LOG(LOG_WARN, &session->context->errout,
212
67
                     "Skytraq 0x62/80: bad len %zu\n", len);
213
67
            return 0;
214
67
        }
215
994
        for (i = 0; i < 6; i++) {
216
852
            u[i] = getub(buf, i + 2);
217
852
        }
218
142
        GPSD_LOG(LOG_PROG, &session->context->errout,
219
142
                 "Skytraq 0x62/80: enable %u ranging %u URA mask %u "
220
142
                 "correction %u chans %u subsystems %u \n",
221
142
                 u[0], u[1], u[2], u[3], u[4], u[5]);
222
142
        break;
223
79
    case  0x81:
224
        // QXSS status
225
79
        u[0] = getub(buf, 2);
226
79
        u[1] = getub(buf, 3);
227
79
        GPSD_LOG(LOG_PROG, &session->context->errout,
228
79
                 "Skytraq 0x62/81: enable %u chans %u\n",
229
79
                 u[0], u[1]);
230
79
        break;
231
111
    case  0x82:
232
        // SBAS advanced status
233
111
        if (46 < len) {
234
43
            GPSD_LOG(LOG_WARN, &session->context->errout,
235
43
                     "Skytraq 0x62/82: bad len %zu\n", len);
236
43
            return 0;
237
43
        }
238
1.56k
        for (i = 0; i < 22; i++) {
239
1.49k
            u[i] = getub(buf, i + 2);
240
1.49k
        }
241
68
        GPSD_LOG(LOG_PROG, &session->context->errout,
242
68
                 "Skytraq 0x62/82: enable %u ranging %u URA %u corr %u "
243
68
                 "chans %u mask x%02x WAAS %u %u %u %u "
244
68
                 "EGNOS %u %u %u %u MSAS %u %u %u %u "
245
68
                 "GAGAN %u %u %u %u\n",
246
68
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
247
68
                 u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16], u[17],
248
68
                 u[18], u[19], u[20], u[21]);
249
68
        break;
250
103
    default:
251
103
        GPSD_LOG(LOG_PROG, &session->context->errout,
252
502
                 "Skytraq 0x62: SID x%02x len %zu\n", sid, len);
253
502
    }
254
392
    return 0;
255
502
}
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
306
{
265
306
    unsigned sid;
266
267
306
    if (3 > len) {
268
214
        GPSD_LOG(LOG_WARN, &session->context->errout,
269
214
                 "Skytraq 0x63: bad len %zu\n", len);
270
214
        return 0;
271
214
    }
272
273
92
    sid = getub(buf, 1);
274
275
    // FIXME: decode them!
276
92
    GPSD_LOG(LOG_PROG, &session->context->errout,
277
92
             "Skytraq 0x63: SID %u\n", sid);
278
92
    return 0;
279
306
}
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
961
{
289
961
    unsigned sid;
290
961
    unsigned u[13];
291
961
    int i;
292
961
    int s[3];
293
294
961
    if (3 > len) {
295
72
        GPSD_LOG(LOG_WARN, &session->context->errout,
296
72
                 "Skytraq 0x64: bad len %zu\n", len);
297
72
        return 0;
298
72
    }
299
300
889
    sid = getub(buf, 1);
301
889
    switch (sid) {
302
48
    case  0x80:
303
        // GNSS Boot status
304
48
        u[0] = getub(buf, 2);
305
48
        u[1] = getub(buf, 3);
306
48
        GPSD_LOG(LOG_PROG, &session->context->errout,
307
48
                 "Skytraq 0x64/80: enable %u type %u\n",
308
48
                 u[0], u[1]);
309
48
        break;
310
133
    case  0x81:
311
        // Extended NMEA Message Interval
312
133
        if (14 > len) {
313
55
            GPSD_LOG(LOG_WARN, &session->context->errout,
314
55
                     "Skytraq 0x64/81: bad len %zu\n", len);
315
55
            return 0;
316
55
        }
317
1.01k
        for (i = 0; i < 12; i++) {
318
936
            u[i] = getub(buf, i + 2);
319
936
        }
320
78
        GPSD_LOG(LOG_PROG, &session->context->errout,
321
78
                 "Skytraq 0x64/81: GGA %u GSA %u GSV %u GLL %u RMC %u "
322
78
                 "VTG %u ZDA %u GNS %u GBS %u GRS %u DTM %u GST %u\n",
323
78
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
324
78
                 u[9], u[10], u[11]);
325
78
        break;
326
72
    case  0x83:
327
        // Interference Detection Status
328
72
        u[0] = getub(buf, 2);
329
72
        u[1] = getub(buf, 3);
330
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
331
72
                 "Skytraq 0x64/83: enable %u status %u\n",
332
72
                 u[0], u[1]);
333
72
        break;
334
58
    case  0x85:
335
        // GPS PARAMETER SEARCH ENGINE NUMBER
336
58
        u[0] = getub(buf, 2);
337
58
        GPSD_LOG(LOG_PROG, &session->context->errout,
338
58
                 "Skytraq 0x64/85: search engine number %u\n", u[0]);
339
58
        break;
340
73
    case  0x88:
341
        // Position/Fix navigation mask
342
73
        u[0] = getub(buf, 2);
343
73
        u[1] = getub(buf, 3);
344
73
        GPSD_LOG(LOG_PROG, &session->context->errout,
345
73
                 "Skytraq 0x64/88: 1st %u subsequent %u\n",
346
73
                 u[0], u[1]);
347
73
        break;
348
50
    case  0x8a:
349
        // GPS UTC Reference time
350
50
        if (7 > len) {
351
0
            GPSD_LOG(LOG_WARN, &session->context->errout,
352
0
                     "Skytraq 0x64/8a: bad len %zu\n", len);
353
0
            return 0;
354
0
        }
355
50
        u[0] = getub(buf, 2);
356
50
        u[1] = getbeu16(buf, 3);
357
50
        u[2] = getub(buf, 5);
358
50
        u[3] = getub(buf, 6);
359
50
        GPSD_LOG(LOG_PROG, &session->context->errout,
360
50
                 "Skytraq 0x64/8a: enable %u year %u month %u day %u\n",
361
50
                 u[0], u[1], u[2], u[3]);
362
50
        break;
363
72
    case  0x8b:
364
        // GNSS Nav mode
365
72
        u[0] = getub(buf, 2);
366
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
367
72
                 "Skytraq 0x64/8b: mode %u\n", u[0]);
368
72
        break;
369
49
    case  0x8c:
370
        // GNSS Constellation type for nav solution
371
49
        u[0] = getbeu16(buf, 2);
372
49
        GPSD_LOG(LOG_PROG, &session->context->errout,
373
49
                 "Skytraq 0x64/8c: Nav Type x%02x\n", u[0]);
374
49
        break;
375
69
    case  0x8e:
376
        // GPS time
377
69
        u[0] = getbeu32(buf, 2);    // TOW ms
378
69
        u[1] = getbeu32(buf, 6);    // TOW ns
379
69
        u[2] = getbeu16(buf, 10);   // GPS week
380
69
        s[0] = getsb(buf, 12);      // default leap s
381
69
        s[1] = getsb(buf, 13);      // current leap s
382
69
        u[3] = getub(buf, 14);      // valid
383
        // FIXME: save GPS week and leap s
384
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
385
69
                 "Skytraq 0x64/8a: TOW %u %u week %u leap %d %d valid x%x\n",
386
69
                 u[0], u[1], u[2], s[0], s[1], u[3]);
387
69
        break;
388
52
    case  0x92:
389
        // GLONASS Time corrections
390
52
        s[0] = getbes32(buf, 2);    // tau c
391
52
        s[1] = getbes32(buf, 6);    // tau gps
392
52
        GPSD_LOG(LOG_PROG, &session->context->errout,
393
52
                 "Skytraq 0x64/92: tau c %d tau GPS %d\n",
394
52
                 s[0], s[1]);
395
52
        break;
396
82
    case  0xfe:
397
        // Version extension string
398
82
        GPSD_LOG(LOG_PROG, &session->context->errout,
399
82
                 "Skytraq 0x64/fe: >%.32s<\n", &buf[2]);
400
82
        break;
401
131
    default:
402
131
        GPSD_LOG(LOG_PROG, &session->context->errout,
403
889
                 "Skytraq 0x64: SID x%02x len %zu\n", sid, len);
404
889
    }
405
834
    return 0;
406
889
}
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
513
{
416
513
    unsigned sid;
417
513
    unsigned u[13];
418
419
513
    if (3 > len) {
420
294
        GPSD_LOG(LOG_WARN, &session->context->errout,
421
294
                 "Skytraq 0x65: bad len %zu\n", len);
422
294
        return 0;
423
294
    }
424
425
219
    sid = getub(buf, 1);
426
219
    switch (sid) {
427
62
    case  0x80:
428
        // 1PPS Pulse width
429
62
        u[0] = getbeu32(buf, 2);    // pulse width miicro seconds
430
62
        GPSD_LOG(LOG_PROG, &session->context->errout,
431
62
                 "Skytraq 0x65/80: width %u\n", u[0]);
432
62
        break;
433
69
    case  0x81:
434
        // PPS2 frequency
435
69
        u[0] = getbeu32(buf, 2);    // freq of PPS2 Hz
436
69
        GPSD_LOG(LOG_PROG, &session->context->errout,
437
69
                 "Skytraq 0x65/81: PPS2 Hz %u\n", u[0]);
438
69
        break;
439
88
    default:
440
88
        GPSD_LOG(LOG_PROG, &session->context->errout,
441
219
                 "Skytraq 0x65: SID x%02x len %zu\n", sid, len);
442
219
    }
443
219
    return 0;
444
219
}
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
535
{
454
535
    unsigned sid;
455
535
    unsigned u[13];
456
535
    double d[5];
457
458
535
    if (3 > len) {
459
188
        GPSD_LOG(LOG_WARN, &session->context->errout,
460
188
                 "Skytraq 0x6A: bad len %zu\n", len);
461
188
        return 0;
462
188
    }
463
464
347
    sid = getub(buf, 1);
465
347
    switch (sid) {
466
71
    case  0x83:
467
        // RTK mode and operational functioN
468
71
        u[0] = getub(buf, 2);                // RTK mode
469
71
        u[1] = getub(buf, 3);                // RTK function
470
71
        u[2] = getbeu32(buf, 4);             // saved survey length
471
71
        u[3] = getbeu32(buf, 8);             // standard deviation
472
71
        d[0] = getled64((const char *)buf, 12);    // latitude
473
71
        d[1] = getled64((const char *)buf, 20);    // longitude
474
71
        d[3] = getlef32((const char *)buf, 28);    // altitude (HAE or MSL?)
475
71
        u[4] = getub(buf, 32);               // runtime function
476
71
        u[5] = getbeu32(buf, 33);            // run-time survey length
477
71
        d[4] = getlef32((const char *)buf, 37);    // baseline length constant
478
71
        GPSD_LOG(LOG_PROG, &session->context->errout,
479
71
                 "Skytraq 0x6A/83: mode %u func %u len %u sdev %u lat %.8f "
480
71
                 "lon %.8f alt %.4f func %u len %u len %.4f\n",
481
71
                 u[0], u[1], u[2], u[3], d[0], d[1], d[3], u[4], u[5], d[4]);
482
71
        break;
483
106
    case  0x85:
484
        // RTK slave base serial port baud ratE
485
106
        u[0] = getub(buf, 2);        // rate code
486
106
        GPSD_LOG(LOG_PROG, &session->context->errout,
487
106
                 "Skytraq 0x6A/85: rate %u\n", u[0]);
488
106
        break;
489
92
    case  0x88:
490
        // RTK kinematic base serial port baud ratE
491
92
        u[0] = getub(buf, 2);        // rate code
492
92
        GPSD_LOG(LOG_PROG, &session->context->errout,
493
92
                 "Skytraq 0x6A/88: rate %u\n", u[0]);
494
92
        break;
495
78
    default:
496
78
        GPSD_LOG(LOG_PROG, &session->context->errout,
497
347
                 "Skytraq 0x6A: SID x%02x len %zu\n", sid, len);
498
347
    }
499
347
    return 0;
500
347
}
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
119
{
510
119
    unsigned sid, ssid;
511
119
    unsigned u[16];
512
119
    double d[2];
513
119
    int i;
514
515
119
    if (3 > len) {
516
68
        GPSD_LOG(LOG_WARN, &session->context->errout,
517
68
                 "Skytraq 0x7A: bad len %zu\n", len);
518
68
        return 0;
519
68
    }
520
521
51
    sid = getub(buf, 1);
522
51
    ssid = getub(buf, 2);
523
51
    switch ((sid << 8) | ssid) {
524
0
    case 0x0e80:
525
        // Moving base software version
526
0
        if (16 > len) {
527
0
            GPSD_LOG(LOG_WARN, &session->context->errout,
528
0
                     "Skytraq 0x7A/0E/80: bad len %zu\n", len);
529
0
            return 0;
530
0
        }
531
0
        for (i = 0; i < 13; i++) {
532
0
            u[i] = getub(buf, i + 3);
533
0
        }
534
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
535
0
                 "Skytraq 0x7A/0E/80: type %u "
536
0
                  "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u\n",
537
0
                 u[0], u[2], u[3], u[4], u[6], u[7], u[8], u[10],
538
0
                 u[11], u[12]);
539
0
        break;
540
0
    case 0x0e81:
541
        // Moving base software CRC
542
0
        u[0] = getub(buf, 3);
543
0
        u[1] = getbeu16(buf, 4);
544
545
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
546
0
                 "Skytraq  0x7A/0E/801: type %u crc %u\n", u[0], u[1]);
547
0
        break;
548
0
    case 0x0e82:
549
        // Moving base pos update rate
550
0
        u[0] = getub(buf, 3);
551
552
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
553
0
                 "Skytraq  0x7A/0E/802: rate %u\n", u[0]);
554
0
        break;
555
0
    case 0x0e83:
556
        // Moving base heading and pitch offsets
557
0
        d[0] = getbeu32(buf, 3);    // heading
558
0
        d[1] = getbeu32(buf, 7);    // pitch
559
560
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
561
0
                 "Skytraq  0x7A/0E/803: heading %f pitch %f\n", d[0], d[1]);
562
0
        break;
563
51
    default:
564
51
        GPSD_LOG(LOG_PROG, &session->context->errout,
565
51
                 "Skytraq 0x7A: SID x%02x/%02x len %zu\n", sid, ssid, len);
566
51
    }
567
51
    return 0;
568
51
}
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
121
{
582
121
    unsigned kver_x;  // kernel version
583
121
    unsigned kver_y;  // kernel version
584
121
    unsigned kver_z;  // kernel version
585
121
    unsigned over_x;  // ODM version
586
121
    unsigned over_y;  // ODM version
587
121
    unsigned over_z;  // ODM version
588
121
    unsigned rev_yy;  // revision
589
121
    unsigned rev_mm;  // revision
590
121
    unsigned rev_dd;  // revision
591
592
121
    if (14 != len) {
593
52
        return 0;
594
52
    }
595
596
69
    kver_x  = getbeu16(buf, 2);
597
69
    kver_y  = getub(buf, 4);
598
69
    kver_z  = getub(buf, 5);
599
69
    over_x  = getbeu16(buf, 6);
600
69
    over_y  = getub(buf, 8);
601
69
    over_z  = getub(buf, 9);
602
69
    rev_yy  = getbeu16(buf, 10);
603
69
    rev_mm  = getub(buf, 12);
604
69
    rev_dd  = getub(buf, 13);
605
606
69
    (void)snprintf(session->subtype, sizeof(session->subtype) - 1,
607
69
                   "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u",
608
69
                   kver_x, kver_y, kver_z,
609
69
                   over_x, over_y, over_z,
610
69
                   rev_yy, rev_mm, rev_dd);
611
612
69
    GPSD_LOG(LOG_PROG, &session->context->errout,
613
69
             "Skytraq 0x80: %s\n",
614
69
             session->subtype);
615
69
    return 0;
616
121
}
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
132
{
626
132
    unsigned type, crc;
627
628
132
    if (4 != len) {
629
73
        GPSD_LOG(LOG_WARN, &session->context->errout,
630
73
                 "Skytraq 0x81: bad len %zu\n", len);
631
73
        return 0;
632
73
    }
633
634
59
    type = getub(buf, 1);
635
59
    crc  = getbeu16(buf, 2);
636
637
59
    GPSD_LOG(LOG_PROG, &session->context->errout,
638
59
             "Skytraq 0x81: type %u crc %u\n", type, crc);
639
59
    return 0;
640
132
}
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
163
{
650
163
    unsigned rate;
651
652
163
    if (2 != len) {
653
94
        GPSD_LOG(LOG_WARN, &session->context->errout,
654
94
                 "Skytraq 0x86: bad len %zu\n", len);
655
94
        return 0;
656
94
    }
657
658
69
    rate = getub(buf, 1);
659
660
69
    GPSD_LOG(LOG_PROG, &session->context->errout,
661
69
             "Skytraq 0x86: rate %u\n", rate);
662
69
    return 0;
663
163
}
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
163
{
673
163
    unsigned i;
674
163
    uint8_t u[7];
675
676
163
    if (8 != len) {
677
93
        GPSD_LOG(LOG_WARN, &session->context->errout,
678
93
                 "Skytraq 0x89: bad len %zu\n", len);
679
93
        return 0;
680
93
    }
681
682
560
    for (i = 0; i < 7; i++) {
683
490
        u[i] = getub(buf, i + 1);
684
490
    }
685
70
    GPSD_LOG(LOG_PROG, &session->context->errout,
686
70
             "Skytraq 0x89: rate %u Meas %u raw %u CH_status %u "
687
70
             "RCV_statas %u subf %u eraw %u\n",
688
70
             u[0], u[1], u[2], u[3], u[4], u[5], u[6]);
689
690
70
    return 0;
691
163
}
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
994
{
701
994
    unsigned i;
702
994
    uint8_t u[15];
703
704
994
    if (16 != len) {
705
927
        GPSD_LOG(LOG_WARN, &session->context->errout,
706
927
                 "Skytraq 0x8A: bad len %zu\n", len);
707
927
        return 0;
708
927
    }
709
710
1.07k
    for (i = 0; i < 15; i++) {
711
1.00k
        u[i] = getub(buf, i + 1);
712
1.00k
    }
713
67
    GPSD_LOG(LOG_PROG, &session->context->errout,
714
67
             "Skytraq 0x8A: enable %u MSM %u 1005 %u 107x %u 108x %u "
715
67
             "109x %u 110x %u 111x %u 112x %u 1019 %u 1020 %u "
716
67
             "1042 %u 1046 %u type %u version %u\n",
717
67
             u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
718
67
             u[8], u[9], u[10], u[11], u[12], u[13], u[14]);
719
720
67
    return 0;
721
994
}
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
200
{
731
200
    uint8_t u[5];
732
200
    double d[3];
733
734
200
    if (35 != len) {
735
79
        GPSD_LOG(LOG_WARN, &session->context->errout,
736
79
                 "Skytraq 0x8B: bad len %zu\n", len);
737
79
        return 0;
738
79
    }
739
740
121
    u[0] = getub(buf, 1);
741
121
    u[1] = getbeu32(buf, 2);
742
121
    u[2] = getbeu32(buf, 6);
743
121
    d[0] = getbed64((const char *)buf, 10);
744
121
    d[1] = getbed64((const char *)buf, 18);
745
121
    d[2] = getbef32((const char *)buf, 26);
746
121
    u[3] = getub(buf, 30);
747
121
    u[4] = getbeu32(buf, 31);
748
749
121
    GPSD_LOG(LOG_PROG, &session->context->errout,
750
121
             "Skytraq 0x8B: saved mode %u saved length %u stddev %u "
751
121
             "lat %.9f lon %.9f HAE %0.4f run mode %u survey len %u\n",
752
121
             u[0], u[1], u[2], d[0], d[1], d[2], u[3], u[4]);
753
754
121
    return 0;
755
200
}
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
194
{
765
194
    unsigned mode;
766
767
194
    if (2 != len) {
768
93
        GPSD_LOG(LOG_WARN, &session->context->errout,
769
93
                 "Skytraq 0x93: bad len %zu\n", len);
770
93
        return 0;
771
93
    }
772
773
101
    mode = getub(buf, 1);
774
775
101
    GPSD_LOG(LOG_PROG, &session->context->errout,
776
101
             "Skytraq 0x93: mode %u\n", mode);
777
101
    return 0;
778
194
}
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
229
{
796
229
    unsigned char  navmode; // Navigation fix mode (0: No fix, 1: 2D, 2: 3D, 3: 3D+DGNSS
797
229
    unsigned short week;    // GNSS week number
798
229
    double   ftow;       // Time of week
799
229
    timespec_t ts_tow;
800
229
    char ts_buf[TIMESPEC_LEN];
801
802
229
    int *mode = &session->newdata.mode;
803
229
    int *status = &session->newdata.status;
804
229
    gps_mask_t mask = 0;
805
806
229
    if (59 != len) {
807
205
        GPSD_LOG(LOG_INF, &session->context->errout, "Skytraq: "
808
205
            "Navigation Data Message has incorrect length %zu\n", len);
809
205
        return 0;
810
205
    }
811
812
24
    navmode = getub(buf, 1);
813
24
    switch (navmode) {
814
3
    case 1: // 2D fix
815
3
        *mode = MODE_2D;
816
3
        *status = STATUS_GPS;
817
3
        break;
818
0
    case 2: // 3D fix
819
0
        *mode = MODE_3D;
820
0
        *status = STATUS_GPS;
821
0
        break;
822
0
    case 3: // 3D DGPS fix
823
0
        *mode = MODE_3D;
824
0
        *status = STATUS_DGPS;
825
0
        break;
826
827
21
    default: // Includes SKY_MODE_NONE
828
21
        *mode = MODE_NO_FIX;
829
21
        *status = STATUS_UNK;
830
21
        break;
831
24
    }
832
24
    mask |= MODE_SET | STATUS_SET;
833
834
24
    session->gpsdata.satellites_used = getub(buf, 2);
835
836
24
    mask |= ONLINE_SET;
837
24
    week = getbeu16(buf,  3);
838
24
    ftow = getbeu32(buf,  5) / 100.0;
839
24
    DTOTS(&ts_tow, (int)ftow);
840
841
24
    session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
842
24
    mask |= TIME_SET;
843
844
24
    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
0
            session->newdata.altHAE  = getbes32(buf, 17) / 100.0;
851
0
            session->newdata.altMSL  = getbes32(buf, 21) / 100.0;
852
853
0
            session->newdata.ecef.x  = getbes32(buf, 35) / 100.0;
854
0
            session->newdata.ecef.y  = getbes32(buf, 39) / 100.0;
855
0
            session->newdata.ecef.z  = getbes32(buf, 43) / 100.0;
856
0
            session->newdata.ecef.vx = getbes32(buf, 47) / 100.0;
857
0
            session->newdata.ecef.vy = getbes32(buf, 51) / 100.0;
858
0
            session->newdata.ecef.vz = getbes32(buf, 55) / 100.0;
859
0
            mask |= ECEF_SET | VECEF_SET | ALTITUDE_SET;
860
0
        }
861
3
    }
862
863
24
    session->gpsdata.dop.gdop = getbeu16(buf, 25) / 100.0;
864
24
    session->gpsdata.dop.pdop = getbeu16(buf, 27) / 100.0;
865
24
    session->gpsdata.dop.hdop = getbeu16(buf, 29) / 100.0;
866
24
    session->gpsdata.dop.vdop = getbeu16(buf, 31) / 100.0;
867
24
    session->gpsdata.dop.tdop = getbeu16(buf, 33) / 100.0;
868
24
    mask |= DOP_SET | CLEAR_IS | REPORT_IS;
869
24
    GPSD_LOG(LOG_DATA, &session->context->errout,
870
24
         "Skytraq: NAVDATA time=%s, lat=%.7f lon=%.7f altHAE=%.2f altMSL=%.2f mode=%d status=%d "
871
24
         "gdop: %.2f, hdop: %.2f, pdop: %.2f, tdop: %.2f, vdop: %.2f\n",
872
24
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
873
24
         session->newdata.latitude,
874
24
         session->newdata.longitude,
875
24
         session->newdata.altHAE,
876
24
         session->newdata.altMSL,
877
24
         session->newdata.mode,
878
24
         session->newdata.status,
879
24
         session->gpsdata.dop.gdop,
880
24
         session->gpsdata.dop.hdop,
881
24
         session->gpsdata.dop.pdop,
882
24
         session->gpsdata.dop.tdop,
883
24
         session->gpsdata.dop.vdop);
884
24
    return mask;
885
24
}
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
145
{
895
145
    unsigned datum;
896
897
145
    if (3 != len) {
898
69
        GPSD_LOG(LOG_WARN, &session->context->errout,
899
69
                 "Skytraq 0xAE: bad len %zu\n", len);
900
69
        return 0;
901
69
    }
902
903
76
    datum  = getbeu16(buf, 1);
904
905
76
    GPSD_LOG(LOG_PROG, &session->context->errout,
906
76
             "Skytraq 0xAE: datum %u\n", datum);
907
76
    return 0;
908
145
}
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
135
{
918
135
    unsigned mode, pdop, hdop, gdop;
919
920
135
    if (8 != len) {
921
69
        GPSD_LOG(LOG_WARN, &session->context->errout,
922
69
                 "Skytraq 0xAF: bad len %zu\n", len);
923
69
        return 0;
924
69
    }
925
926
66
    mode = getub(buf, 1);
927
66
    pdop = getbeu16(buf, 2);
928
66
    hdop = getbeu16(buf, 4);
929
66
    gdop = getbeu16(buf, 6);
930
931
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
932
66
             "Skytraq 0xAF: Masks: mode %u pdop %u hdop %u gdop %u\n",
933
66
             mode, pdop, hdop, gdop);
934
66
    return 0;
935
135
}
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
168
{
945
168
    unsigned select, elevation, cnr;
946
947
168
    if (4 != len) {
948
89
        GPSD_LOG(LOG_WARN, &session->context->errout,
949
89
                 "Skytraq 0xB0: bad len %zu\n", len);
950
89
        return 0;
951
89
    }
952
953
79
    select = getub(buf, 1);
954
79
    elevation = getub(buf, 2);
955
79
    cnr = getub(buf, 3);
956
957
79
    GPSD_LOG(LOG_PROG, &session->context->errout,
958
79
             "Skytraq 0xB0: select %u el %u cnr %u\n",
959
79
             select, elevation, cnr);
960
79
    return 0;
961
168
}
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
168
{
971
168
    unsigned status, pspeed, pcnt, uspeed, ucnt, udist;
972
973
168
    if (12 != len) {
974
95
        GPSD_LOG(LOG_WARN, &session->context->errout,
975
95
                 "Skytraq 0xB4: bad len %zu\n", len);
976
95
        return 0;
977
95
    }
978
979
73
    status = getub(buf, 1);
980
73
    pspeed = getbeu16(buf, 2);
981
73
    pcnt = getbeu16(buf, 4);
982
73
    uspeed = getbeu16(buf, 6);
983
73
    ucnt = getbeu16(buf, 8);
984
73
    udist = getbeu16(buf, 10);
985
986
73
    GPSD_LOG(LOG_PROG, &session->context->errout,
987
73
             "Skytraq 0xB4: status %u pspeed %u pcnt %u uspeed %u "
988
73
             "ucnt %u udist %u\n",
989
73
             status, pspeed, pcnt, uspeed, ucnt, udist);
990
73
    return 0;
991
168
}
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
177
{
1001
177
    unsigned mode;
1002
1003
177
    if (2 != len) {
1004
106
        GPSD_LOG(LOG_WARN, &session->context->errout,
1005
106
                 "Skytraq 0xB9: bad len %zu\n", len);
1006
106
        return 0;
1007
106
    }
1008
1009
71
    mode = getub(buf, 1);
1010
1011
71
    GPSD_LOG(LOG_PROG, &session->context->errout,
1012
71
             "Skytraq 0xB9: mode %u\n", mode);
1013
71
    return 0;
1014
177
}
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
73
{
1024
73
    int delay;
1025
1026
73
    if (5 != len) {
1027
73
        GPSD_LOG(LOG_WARN, &session->context->errout,
1028
73
                 "Skytraq 0xBB: bad len %zu\n", len);
1029
73
        return 0;
1030
73
    }
1031
1032
0
    delay = getbeu32(buf, 1);
1033
1034
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1035
0
             "Skytraq 0xBB: delay %d\n", delay);
1036
0
    return 0;
1037
73
}
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
355
{
1047
355
    unsigned iod;   // Issue of data 0 - 255
1048
355
    unsigned wn;    // week number 0 - 65535
1049
355
    unsigned tow;   // receiver tow 0 - 604799999 in mS
1050
355
    unsigned mp;    // measurement period 1 - 1000 ms
1051
355
    char ts_buf[TIMESPEC_LEN];
1052
355
    timespec_t ts_tow;
1053
1054
355
    if (10 != len) {
1055
254
        return 0;
1056
254
    }
1057
1058
101
    iod = (unsigned)getub(buf, 1);
1059
101
    wn = getbeu16(buf, 2);
1060
101
    tow = getbeu32(buf, 4);
1061
101
    mp = getbeu16(buf, 8);
1062
101
    MSTOTS(&ts_tow, tow);
1063
1064
    // should this be newdata.skyview_time?
1065
101
    session->gpsdata.skyview_time = gpsd_gpstime_resolv(session, wn, ts_tow);
1066
1067
101
    GPSD_LOG(LOG_DATA, &session->context->errout,
1068
101
             "Skytraq 0xDC: iod %u wn %u tow %u mp %u t%s\n",
1069
101
             iod, wn, tow, mp,
1070
101
             timespec_str(&session->gpsdata.skyview_time, ts_buf,
1071
101
                          sizeof(ts_buf)));
1072
101
    return 0;
1073
355
}
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
884
{
1082
884
    unsigned i;     // generic loop variable
1083
1084
    // buf[0] os the message ID
1085
    // Issue of data 0 - 255
1086
884
    unsigned iod = getub(buf, 1);
1087
    // number of measurements
1088
884
    unsigned nmeas = getub(buf, 2);
1089
1090
884
    GPSD_LOG(LOG_DATA, &session->context->errout,
1091
884
             "Skytraq 0xDD: iod %u, nmeas %u len %zd\n",
1092
884
             iod, nmeas, len);
1093
1094
884
    if (SKY_CHANNELS < nmeas) {
1095
3
        GPSD_LOG(LOG_WARN, &session->context->errout,
1096
3
                "Skytraq 0x0D: too many measurements %d\n",
1097
3
                 nmeas);
1098
3
        nmeas = SKY_CHANNELS;
1099
3
    }
1100
1101
884
    if (len < (3 + (nmeas * 23))) {
1102
75
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1103
75
                 "Skytraq 0xDD: bad len %zd, s/b %d\n",
1104
75
                 len, 3 + (nmeas & 23));
1105
75
        return 0;
1106
75
    }
1107
1108
    // check IOD?
1109
809
    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
809
    memset(session->gpsdata.raw.meas, 0, sizeof(session->gpsdata.raw.meas));
1114
1115
3.36k
    for (i = 0; i < nmeas; i++) {
1116
2.55k
        const char *obs_code;
1117
2.55k
        int off = 3 + (23 * i);
1118
1119
2.55k
        uint8_t PRN = getub(buf, off + 0);
1120
        // carrier-to-noise density ratio dB-Hz
1121
2.55k
        uint8_t cno = getub(buf, off + 1);
1122
        // pseudorange in meters
1123
2.55k
        double prMes = getbed64((const char *)buf, off + 2);
1124
        // carrier phase in cycles
1125
2.55k
        double cpMes = getbed64((const char *)buf, off + 10);
1126
        // doppler in Hz, positive towards sat
1127
2.55k
        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
2.55k
        uint8_t trkStat = getub(buf, off + 22);
1137
2.55k
        uint8_t gnssId = 0;
1138
        // svId should always be from 0 to 99, except SBAS.
1139
2.55k
        uint8_t svId = 0;
1140
2.55k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1141
1142
2.55k
        session->gpsdata.raw.meas[i].gnssid = gnssId;
1143
2.55k
        switch (gnssId) {
1144
1.56k
        case 0:       // GPS
1145
1.56k
            FALLTHROUGH
1146
1.56k
        case 5:       // QZSS
1147
1.56k
            FALLTHROUGH
1148
1.63k
        case 20:      // IRNSS, just guessing here
1149
1.63k
            obs_code = "L1C";       // u-blox calls this L1C/A ?
1150
1.63k
            break;
1151
375
        case 1:       // SBAS
1152
375
            if (100 < svId) {
1153
                // Should be always true, but pacify Coverity 498045
1154
375
                svId -= 100;            // adjust for RINEX 3 svid
1155
375
            }
1156
375
            obs_code = "L1C";       // u-blox calls this L1C/A
1157
375
            break;
1158
0
        case 2:       // GALILEO
1159
0
            obs_code = "L1B";       // u-blox calls this E1OS
1160
0
            break;
1161
143
        case 3:       // BeiDou
1162
143
            obs_code = "L2I";       // u-blox calls this B1I
1163
143
            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
408
        case 6:       // GLONASS
1170
408
            obs_code = "L1C";       // u-blox calls this L1OF
1171
408
            break;
1172
2.55k
        }
1173
2.55k
        (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code,
1174
2.55k
                      sizeof(session->gpsdata.raw.meas[i].obs_code));
1175
1176
2.55k
        session->gpsdata.raw.meas[i].svid = svId;
1177
2.55k
        session->gpsdata.raw.meas[i].snr = cno;
1178
2.55k
        session->gpsdata.raw.meas[i].satstat = trkStat;
1179
2.55k
        if (trkStat & 1) {
1180
            // prMes valid
1181
1.53k
            session->gpsdata.raw.meas[i].pseudorange = prMes;
1182
1.53k
        } else {
1183
1.02k
            session->gpsdata.raw.meas[i].pseudorange = NAN;
1184
1.02k
        }
1185
2.55k
        if (trkStat & 2) {
1186
            // doppler valid
1187
1.39k
            session->gpsdata.raw.meas[i].doppler = doMes;
1188
1.39k
        } else {
1189
1.16k
            session->gpsdata.raw.meas[i].doppler = NAN;
1190
1.16k
        }
1191
2.55k
        if (trkStat & 4) {
1192
            // cpMes valid
1193
1.23k
            session->gpsdata.raw.meas[i].carrierphase = cpMes;
1194
1.31k
        } else {
1195
1.31k
            session->gpsdata.raw.meas[i].carrierphase = NAN;
1196
1.31k
        }
1197
2.55k
        session->gpsdata.raw.meas[i].codephase = NAN;
1198
2.55k
        session->gpsdata.raw.meas[i].deltarange = NAN;
1199
        // skytraq does not report locktime, so assume max
1200
2.55k
        session->gpsdata.raw.meas[i].locktime = LOCKMAX;
1201
2.55k
        if (trkStat & 8) {
1202
            // possible slip
1203
1.17k
            session->gpsdata.raw.meas[i].lli = 2;
1204
1.17k
        }
1205
2.55k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1206
2.55k
                 "PRN %u (%u:%u) prMes %f cpMes %f doMes %f\n"
1207
2.55k
                 "cno %u  rtkStat %u\n", PRN,
1208
2.55k
                 gnssId, svId, prMes, cpMes, doMes, cno, trkStat);
1209
1210
2.55k
    }
1211
1212
    // return RAW_IS;  // WIP
1213
809
    return 0;
1214
809
}
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
988
{
1224
988
    int st, nsv;
1225
988
    unsigned i;
1226
988
    unsigned iod;   // Issue of data 0 - 255
1227
988
    unsigned nsvs;  // number of SVs in this packet
1228
1229
988
    if (3 > len) {
1230
177
        GPSD_LOG(LOG_WARN, &session->context->errout,
1231
177
                 "Skytraq 0xDE: bad len %zu\n", len);
1232
177
        return 0;
1233
177
    }
1234
811
    iod = getub(buf, 1);
1235
811
    nsvs = getub(buf, 2);
1236
    // too many sats?
1237
811
    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
745
    if (len < (3 + (nsvs * 10))) {
1245
57
        GPSD_LOG(LOG_WARN, &session->context->errout,
1246
57
                 "Skytraq 0xDE: bad len %zu\n", len);
1247
57
        return 0;
1248
57
    }
1249
688
    gpsd_zero_satellites(&session->gpsdata);
1250
3.49k
    for (i = st = nsv =  0; i < nsvs; i++) {
1251
        // offset into buffer of start of this sat
1252
2.80k
        unsigned off = 3 + (10 * i);
1253
2.80k
        bool good;               // do we have a good record ?
1254
2.80k
        unsigned sv_stat;
1255
2.80k
        unsigned chan_stat;
1256
2.80k
        unsigned ura;
1257
2.80k
        short PRN;
1258
2.80k
        uint8_t gnssId = 0;
1259
2.80k
        uint8_t svId = 0;
1260
1261
2.80k
        PRN = (short)getub(buf, off + 1);
1262
        // fit into gnssid:svid
1263
2.80k
        if (0 == PRN) {
1264
            // skip 0 PRN
1265
312
            continue;
1266
312
        }
1267
2.49k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1268
1269
2.49k
        session->gpsdata.skyview[st].gnssid = gnssId;
1270
2.49k
        session->gpsdata.skyview[st].svid = svId;
1271
2.49k
        session->gpsdata.skyview[st].PRN = PRN;
1272
1273
2.49k
        sv_stat = getub(buf, off + 2);
1274
2.49k
        ura = getub(buf, off + 3);
1275
2.49k
        session->gpsdata.skyview[st].ss = (double)getub(buf, off + 4);
1276
2.49k
        session->gpsdata.skyview[st].elevation =
1277
2.49k
            (double)getbes16(buf, off + 5);
1278
2.49k
        session->gpsdata.skyview[st].azimuth =
1279
2.49k
            (double)getbes16(buf, off + 7);
1280
2.49k
        chan_stat = getub(buf, off + 9);
1281
1282
2.49k
        session->gpsdata.skyview[st].used = (bool)(chan_stat & 0x30);
1283
2.49k
        good = session->gpsdata.skyview[st].PRN != 0 &&
1284
2.49k
            session->gpsdata.skyview[st].azimuth != 0 &&
1285
2.35k
            session->gpsdata.skyview[st].elevation != 0;
1286
1287
2.49k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1288
2.49k
                 "Skytraq PRN=%2d El=%4.0f Az=%5.0f ss=%3.2f stat=%02x,%02x "
1289
2.49k
                 "ura=%d %c\n",
1290
2.49k
                 session->gpsdata.skyview[st].PRN,
1291
2.49k
                 session->gpsdata.skyview[st].elevation,
1292
2.49k
                 session->gpsdata.skyview[st].azimuth,
1293
2.49k
                 session->gpsdata.skyview[st].ss,
1294
2.49k
                 chan_stat, sv_stat, ura,
1295
2.49k
                 good ? '*' : ' ');
1296
1297
2.49k
        if (good) {
1298
2.25k
            if (session->gpsdata.skyview[st].used) {
1299
1.57k
                nsv++;
1300
1.57k
            }
1301
2.25k
            st += 1;
1302
2.25k
        }
1303
2.49k
    }
1304
1305
688
    session->gpsdata.satellites_visible = st;
1306
688
    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
688
    session->gpsdata.satellites_used = nsv;
1313
1314
688
    GPSD_LOG(LOG_DATA, &session->context->errout,
1315
688
             "Skytraq 0xDE: nsvs=%u visible=%u iod=%u\n", nsvs,
1316
688
             session->gpsdata.satellites_visible, iod);
1317
688
    return SATELLITE_SET | USED_IS;
1318
745
}
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
67
{
1328
67
    unsigned short navstat;
1329
67
    unsigned iod;           // Issue of data 0 - 255
1330
67
    unsigned wn;            // week number 0 - 65535
1331
67
    double f_tow;           // receiver tow Sec
1332
67
    double clock_bias;
1333
67
    double clock_drift;
1334
67
    gps_mask_t mask = 0;
1335
67
    timespec_t ts_tow;
1336
67
    char ts_buf[TIMESPEC_LEN];
1337
1338
67
    if (81 != len) {
1339
67
        return 0;
1340
67
    }
1341
1342
0
    iod = (unsigned)getub(buf, 1);
1343
1344
    // fix status is byte 2
1345
0
    navstat = (unsigned short)getub(buf, 2);
1346
0
    session->newdata.status = STATUS_UNK;
1347
0
    session->newdata.mode = MODE_NO_FIX;
1348
0
    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
0
    case 3:
1357
0
        session->newdata.status = STATUS_GPS;
1358
0
        session->newdata.mode = MODE_3D;
1359
0
        break;
1360
0
    case 4:
1361
0
        session->newdata.status = STATUS_DGPS;
1362
0
        session->newdata.mode = MODE_3D;
1363
0
        break;
1364
0
    default:
1365
0
        break;
1366
0
    }
1367
1368
0
    wn = getbeu16(buf, 3);
1369
0
    f_tow = getbed64((const char *)buf, 5);
1370
0
    DTOTS(&ts_tow, f_tow);
1371
1372
    // position/velocity is bytes 13-48, meters and m/s
1373
0
    session->newdata.ecef.x = (double)getbed64((const char *)buf, 13),
1374
0
    session->newdata.ecef.y = (double)getbed64((const char *)buf, 21),
1375
0
    session->newdata.ecef.z = (double)getbed64((const char *)buf, 29),
1376
0
    session->newdata.ecef.vx = (double)getbef32((const char *)buf, 37),
1377
0
    session->newdata.ecef.vy = (double)getbef32((const char *)buf, 41),
1378
0
    session->newdata.ecef.vz = (double)getbef32((const char *)buf, 45);
1379
0
    mask |= ECEF_SET | VECEF_SET;
1380
1381
0
    clock_bias = getbed64((const char *)buf, 49);
1382
0
    clock_drift = getbes32(buf, 57);
1383
1384
0
    session->gpsdata.dop.gdop = getbef32((const char *)buf, 61);
1385
0
    session->gpsdata.dop.pdop = getbef32((const char *)buf, 65);
1386
0
    session->gpsdata.dop.hdop = getbef32((const char *)buf, 69);
1387
0
    session->gpsdata.dop.vdop = getbef32((const char *)buf, 73);
1388
0
    session->gpsdata.dop.tdop = getbef32((const char *)buf, 77);
1389
0
    mask |= DOP_SET;
1390
1391
0
    session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow );
1392
1393
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
1394
0
             "Skytraq 0xDF: iod=%u, stat=%u, wn=%u, tow=%f, t=%s "
1395
0
             "cb: %f, cd: %f "
1396
0
             "gdop: %.2f, pdop: %.2f, hdop: %.2f, vdop: %.2f, tdop: %.2f\n",
1397
0
             iod, navstat, wn, f_tow,
1398
0
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1399
0
             clock_bias, clock_drift,
1400
0
             session->gpsdata.dop.gdop,
1401
0
             session->gpsdata.dop.pdop,
1402
0
             session->gpsdata.dop.hdop,
1403
0
             session->gpsdata.dop.vdop,
1404
0
             session->gpsdata.dop.tdop);
1405
1406
0
    mask |= TIME_SET | STATUS_SET | MODE_SET | CLEAR_IS | REPORT_IS;
1407
0
    return mask;
1408
0
}
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
159
{
1419
159
    int i;
1420
159
    unsigned prn;   // GPS sat PRN
1421
159
    unsigned subf;  // subframe 1-5
1422
    // the words are preprocessed, not raw, just the 24bits of data
1423
159
    uint32_t words[10];  // subframe 1-5
1424
1425
159
    if (33 != len) {
1426
71
        return 0;
1427
71
    }
1428
1429
88
    prn = (unsigned)getub(buf, 1);
1430
88
    subf = (unsigned)getub(buf, 2);
1431
968
    for (i = 0; i < 10; i++) {
1432
880
        words[i] = (uint32_t)getbeu24(buf, 3 + (i * 3));
1433
880
    }
1434
1435
88
    GPSD_LOG(LOG_DATA, &session->context->errout,
1436
88
             "Skytraq 0xE0: prn=%u, subf=%u\n",
1437
88
             prn, subf);
1438
1439
    // could be SBAS?
1440
88
    return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words);
1441
159
}
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
67
{
1455
67
    int i;
1456
67
    unsigned prn;   // BeidouPS sat PRN 206-214
1457
67
    unsigned subf;  // subframe 1-5
1458
    // the words are preprocessed, not raw, just the 28 bytes of data
1459
67
    uint8_t bytes[28];  // raw data
1460
1461
67
    if (31 != len) {
1462
67
        return 0;
1463
67
    }
1464
1465
0
    prn = (unsigned)getub(buf, 1);
1466
0
    subf = (unsigned)getub(buf, 2);
1467
0
    for (i = 0; i < 28; i++) {
1468
0
        bytes[i] = getub(buf, 3 + i);
1469
0
    }
1470
1471
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1472
0
             "Skytraq Beidou D1 subframe PRN %d Subframe %d "
1473
0
             "length %zd byte:%s\n",
1474
0
             prn, subf, len,
1475
0
             gps_hexdump(session->msgbuf, sizeof(session->msgbuf),
1476
0
                         bytes, 28));
1477
1478
0
    return ONLINE_SET;
1479
67
}
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
126
{
1493
126
    int i;
1494
126
    unsigned prn;   // BeidouPS sat PRN 201-205
1495
126
    unsigned subf;  // subframe 1-5
1496
    // the words are preprocessed, not raw, just the 28 bytes of data
1497
126
    uint8_t bytes[28];  // raw data
1498
1499
126
    if (31 != len) {
1500
126
        return 0;
1501
126
    }
1502
1503
0
    prn = (unsigned)getub(buf, 1);
1504
0
    subf = (unsigned)getub(buf, 2);
1505
0
    for (i = 0; i < 28; i++) {
1506
0
        bytes[i] = getub(buf, 3 + i);
1507
0
    }
1508
1509
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
1510
0
             "Skytraq Beidou D2 subframe PRN %d Subframe %d "
1511
0
             "length %zd byte:%s\n",
1512
0
             prn, subf, len,
1513
0
             gps_hexdump(session->msgbuf, sizeof(session->msgbuf),
1514
0
                         bytes, 28));
1515
1516
0
    return ONLINE_SET;
1517
126
}
1518
1519
1520
static gps_mask_t sky_parse(struct gps_device_t * session,
1521
                            unsigned char *buf, size_t len)
1522
10.5k
{
1523
10.5k
    gps_mask_t mask = 0;
1524
1525
10.5k
    if (0 == len) {
1526
0
        return mask;
1527
0
    }
1528
1529
10.5k
    buf += 4;   // skip the leaders and length
1530
10.5k
    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
10.5k
    switch (buf[0]) {
1539
582
    case 0x62:
1540
582
        mask = sky_msg_62(session, buf, len);
1541
582
        break;
1542
306
    case 0x63:
1543
306
        mask = sky_msg_63(session, buf, len);
1544
306
        break;
1545
961
    case 0x64:
1546
961
        mask = sky_msg_64(session, buf, len);
1547
961
        break;
1548
513
    case 0x65:
1549
513
        mask = sky_msg_65(session, buf, len);
1550
513
        break;
1551
535
    case 0x6A:
1552
535
        mask = sky_msg_6A(session, buf, len);
1553
535
        break;
1554
119
    case 0x7A:
1555
119
        mask = sky_msg_7A(session, buf, len);
1556
119
        break;
1557
121
    case 0x80:
1558
        // 128
1559
121
        mask = sky_msg_80(session, buf, len);
1560
121
        break;
1561
132
    case 0x81:
1562
        // Software CRC
1563
132
        mask = sky_msg_81(session, buf, len);
1564
132
        break;
1565
710
    case 0x83:
1566
        // 131 - ACK
1567
710
        if (2 == len) {
1568
138
            GPSD_LOG(LOG_PROG, &session->context->errout,
1569
138
                     "Skytraq 0x83: ACK MID %#02x\n", buf[1]);
1570
572
        } else if (3 == len) {
1571
217
            GPSD_LOG(LOG_PROG, &session->context->errout,
1572
217
                     "Skytraq 0x83: ACK MID %#02x/%02x\n", buf[1], buf[2]);
1573
355
        } else if (4 <= len) {
1574
213
            GPSD_LOG(LOG_PROG, &session->context->errout,
1575
213
                     "Skytraq 0x83: ACK MID %#02x/%02x/%02x\n",
1576
213
                     buf[1], buf[2], buf[3]);
1577
213
        } else {
1578
142
            GPSD_LOG(LOG_PROG, &session->context->errout,
1579
142
                     "Skytraq 0x83: ACK\n");
1580
142
        }
1581
710
        break;
1582
688
    case 0x84:
1583
        // 132 - NACK
1584
688
        if (2 == len) {
1585
279
            GPSD_LOG(LOG_INF, &session->context->errout,
1586
279
                     "Skytraq 0x84: NACK MID %#02x\n", buf[1]);
1587
409
        } else if (3 == len) {
1588
84
            GPSD_LOG(LOG_INF, &session->context->errout,
1589
84
                     "Skytraq 0x84: NACK MID %#02x/%02x\n", buf[1], buf[2]);
1590
325
        } else if (4 <= len) {
1591
78
            GPSD_LOG(LOG_INF, &session->context->errout,
1592
78
                     "Skytraq 0x84: NACK MID %#02x/%02x/x%02x\n",
1593
78
                     buf[1], buf[2], buf[3]);
1594
247
        } else {
1595
247
            GPSD_LOG(LOG_INF, &session->context->errout,
1596
247
                     "Skytraq 0x84: NACK\n");
1597
247
        }
1598
688
        break;
1599
163
    case 0x86:
1600
        // 134 Position Update Rate
1601
163
        mask = sky_msg_86(session, buf, len);
1602
163
        break;
1603
163
    case 0x89:
1604
163
        mask = sky_msg_89(session, buf, len);
1605
163
        break;
1606
994
    case 0x8A:
1607
994
        mask = sky_msg_8A(session, buf, len);
1608
994
        break;
1609
200
    case 0x8B:
1610
200
        mask = sky_msg_8B(session, buf, len);
1611
200
        break;
1612
194
    case 0x93:
1613
        // NMEA TALKER id
1614
194
        mask = sky_msg_93(session, buf, len);
1615
194
        break;
1616
1617
229
    case 0xA8:
1618
        // Navigation Data Message
1619
229
        mask = sky_msg_A8(session, buf, len);
1620
229
        break;
1621
1622
145
    case 0xae:
1623
        // GNSS Datum
1624
145
        mask = sky_msg_AE(session, buf, len);
1625
145
        break;
1626
1627
135
    case 0xaf:
1628
        // DOP Mask
1629
135
        mask = sky_msg_AF(session, buf, len);
1630
135
        break;
1631
1632
168
    case 0xb0:
1633
        // Elevation and CNR mask
1634
168
        mask = sky_msg_B0(session, buf, len);
1635
168
        break;
1636
1637
168
    case 0xb4:
1638
        // Position Pinning Status
1639
168
        mask = sky_msg_B4(session, buf, len);
1640
168
        break;
1641
1642
177
    case 0xb9:
1643
177
        mask = sky_msg_B9(session, buf, len);
1644
177
        break;
1645
1646
73
    case 0xbb:
1647
73
        mask = sky_msg_BB(session, buf, len);
1648
73
        break;
1649
1650
355
    case 0xDC:
1651
        // 220
1652
355
        mask = sky_msg_DC(session, buf, len);
1653
355
        break;
1654
1655
1.01k
    case 0xDD:
1656
        // 221
1657
1.01k
        if (3 <= len) {
1658
884
            mask = sky_msg_DD(session, buf, len);
1659
884
        }
1660
1.01k
        break;
1661
1662
988
    case 0xDE:
1663
        // 222
1664
988
        mask = sky_msg_DE(session, buf, len);
1665
988
        break;
1666
1667
67
    case 0xDF:
1668
        // 223 - Nave status (PVT)
1669
67
        mask = sky_msg_DF(session, buf, len);
1670
67
        break;
1671
1672
159
    case 0xE0:
1673
        // 224
1674
159
        mask = sky_msg_E0(session, buf, len);
1675
159
        break;
1676
1677
67
    case 0xE2:
1678
        // 226 - Beidou2 D1 Subframe data
1679
67
        mask = sky_msg_E2(session, buf, len);
1680
67
        break;
1681
1682
126
    case 0xE3:
1683
        // 227 - Beidou2 D2 Subframe data
1684
126
        mask = sky_msg_E3(session, buf, len);
1685
126
        break;
1686
1687
52
    case 0x67:   // sub-id messages
1688
52
        FALLTHROUGH
1689
115
    case 0x6F:   // sub-id messages
1690
115
        GPSD_LOG(LOG_PROG, &session->context->errout,
1691
115
                 "Skytraq Unknown MID x%02x SID x%02x length %zd\n",
1692
115
                 buf[0], buf[1], len);
1693
115
        break;
1694
215
    default:
1695
215
        GPSD_LOG(LOG_PROG, &session->context->errout,
1696
215
                 "Skytraq Unknown MID %#02x length %zd\n",
1697
215
                 buf[0], len);
1698
215
        break;
1699
10.5k
    }
1700
10.5k
    return mask;
1701
10.5k
}
1702
1703
static gps_mask_t skybin_parse_input(struct gps_device_t *session)
1704
12.2k
{
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
12.2k
    if (UINT_MAX != session->cfg_stage) {
1712
4.71k
        session->cfg_step++;
1713
4.71k
    }
1714
1715
12.2k
    if (UINT_MAX != session->cfg_stage &&
1716
4.71k
        3 <= session->cfg_step) {
1717
        // more init to do
1718
1719
1.54k
        session->cfg_stage++;
1720
1.54k
        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.54k
        switch (session->cfg_stage) {
1728
57
        case 1:
1729
            // Send MID 0x3, to get back MID 0x81 Software CRC
1730
57
            (void)sky_write(session, "\xA0\xA1\x00\x02\x03\x00\x03\x0d\x0a",
1731
57
                            9);
1732
57
            break;
1733
65
        case 2:
1734
            // Send MID 0x10, to get back MID 0x86 (Position Update Rate)
1735
65
            (void)sky_write(session, "\xA0\xA1\x00\x01\x10\x10\x0d\x0a", 8);
1736
65
            break;
1737
69
        case 3:
1738
            // Send MID 0x15, to get back MID 0xB9 (Power Mode Status)
1739
69
            (void)sky_write(session, "\xA0\xA1\x00\x01\x15\x15\x0d\x0a", 8);
1740
69
            break;
1741
70
        case 4:
1742
            // Send MID 0x1f, to get back MID 0x89 Measurement data statuS
1743
70
            (void)sky_write(session, "\xA0\xA1\x00\x01\x1f\x1f\x0d\x0a", 8);
1744
70
            break;
1745
74
        case 5:
1746
            // Send MID 0x21, to get back MID 0x8a  RTCM Data output status
1747
74
            (void)sky_write(session, "\xA0\xA1\x00\x01\x21\x21\x0d\x0a", 8);
1748
74
            break;
1749
70
        case 6:
1750
            // Send MID 0x23, to get back MID 0x8B (Base Position)
1751
70
            (void)sky_write(session, "\xA0\xA1\x00\x01\x23\x23\x0d\x0a", 8);
1752
70
            break;
1753
70
        case 7:
1754
            // Send MID 0x2d, to get back MID 0xAE (GNSS Datum)
1755
70
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2d\x2d\x0d\x0a", 8);
1756
70
            break;
1757
71
        case 8:
1758
            // Send MID 0x2E, to get back MID 0xAF (DOP Mask)
1759
71
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2e\x2e\x0d\x0a", 8);
1760
71
            break;
1761
76
        case 9:
1762
            // Send MID 0x2F, to get back MID 0x80 Elevation and SNR mask
1763
76
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2f\x2f\x0d\x0a", 8);
1764
76
            break;
1765
73
        case 10:
1766
            // Send MID 0x3a, to get back MID 0xb4 Position Pinning
1767
73
            (void)sky_write(session, "\xA0\xA1\x00\x01\x3a\x3a\x0d\x0a", 8);
1768
73
            break;
1769
75
        case 11:
1770
            // Send MID 0x44, to get back MID 0xc2 1PPS timing
1771
            // Timing mode versions only
1772
75
            (void)sky_write(session, "\xA0\xA1\x00\x01\x44\x44\x0d\x0a", 8);
1773
75
            break;
1774
68
        case 12:
1775
            // Send MID 0x46, to get back MID 0xbb 1PPS delay
1776
68
            (void)sky_write(session, "\xA0\xA1\x00\x01\x46\x46\x0d\x0a", 8);
1777
68
            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
59
        case 14:
1783
            // Send MID 0x56, to get back MID 0xc3 1PPS Output Mode
1784
            // Timing mode versions only
1785
59
            (void)sky_write(session, "\xA0\xA1\x00\x01\x56\x56\x0d\x0a", 8);
1786
59
            break;
1787
55
        case 15:
1788
            // Send MID 0x62/02, to get back MID 0x62/80 SBAS status
1789
55
            (void)sky_write(session,
1790
55
                            "\xA0\xA1\x00\x02\x62\x02\x60\x0d\x0a", 9);
1791
55
            break;
1792
53
        case 16:
1793
            // Send MID 0x62/04, to get back MID 0x62/81 QZSS status
1794
53
            (void)sky_write(session,
1795
53
                            "\xA0\xA1\x00\x02\x62\x04\x66\x0d\x0a", 9);
1796
53
            break;
1797
44
        case 17:
1798
            // Send MID 0x62/06, to get back MID 0x62/82 SBAS Advanced status
1799
44
            (void)sky_write(session,
1800
44
                            "\xA0\xA1\x00\x02\x62\x06\x64\x0d\x0a", 9);
1801
44
            break;
1802
41
        case 18:
1803
            // Send MID 0x63/02, to get back MID 0x62/80 SAEE Status
1804
            // not on PX1172RH_DS
1805
41
            (void)sky_write(session,
1806
41
                            "\xA0\xA1\x00\x02\x63\x02\x61\x0d\x0a", 9);
1807
41
            break;
1808
39
        case 19:
1809
            // Send MID 0x64/01, to get back MID 0x64/80
1810
39
            (void)sky_write(session,
1811
39
                            "\xA0\xA1\x00\x02\x64\x01\x65\x0d\x0a", 9);
1812
39
            break;
1813
36
        case 20:
1814
            // Send MID 0x64/03, to get back MID 0x64/81
1815
36
            (void)sky_write(session,
1816
36
                            "\xA0\xA1\x00\x02\x64\x03\x67\x0d\x0a", 9);
1817
36
            break;
1818
33
        case 21:
1819
            // Send MID 0x64/07, to get back MID 0x64/83
1820
33
            (void)sky_write(session,
1821
33
                            "\xA0\xA1\x00\x02\x64\x07\x63\x0d\x0a", 9);
1822
33
            break;
1823
31
        case 22:
1824
            // Send MID 0x64/0b, to get back MID 0x64/85
1825
31
            (void)sky_write(session,
1826
31
                            "\xA0\xA1\x00\x02\x64\x0b\x6f\x0d\x0a", 9);
1827
31
            break;
1828
29
        case 23:
1829
            // Send MID 0x64/12, to get back MID 0x64/88
1830
29
            (void)sky_write(session,
1831
29
                            "\xA0\xA1\x00\x02\x64\x12\x76\x0d\x0a", 9);
1832
29
            break;
1833
26
        case 24:
1834
            // Send MID 0x64/16, to get back MID 0x64/8a
1835
26
            (void)sky_write(session,
1836
26
                            "\xA0\xA1\x00\x02\x64\x16\x72\x0d\x0a", 9);
1837
26
            break;
1838
24
        case 25:
1839
            // Send MID 0x64/18, to get back MID 0x64/8b
1840
24
            (void)sky_write(session,
1841
24
                            "\xA0\xA1\x00\x02\x64\x18\x7c\x0d\x0a", 9);
1842
24
            break;
1843
21
        case 26:
1844
            // Send MID 0x64/1a, to get back MID 0x64/8c
1845
21
            (void)sky_write(session,
1846
21
                            "\xA0\xA1\x00\x02\x64\x1a\x7e\x0d\x0a", 9);
1847
21
            break;
1848
17
        case 27:
1849
            // Send MID 0x64/20, to get back MID 0x64/8e
1850
17
            (void)sky_write(session,
1851
17
                            "\xA0\xA1\x00\x02\x64\x20\x44\x0d\x0a", 9);
1852
17
            break;
1853
15
        case 28:
1854
            // Send MID 0x64/22, to get back MID 0x64/8f
1855
            // not on PX1172RH_DS
1856
15
            (void)sky_write(session,
1857
15
                            "\xA0\xA1\x00\x02\x64\x22\x46\x0d\x0a", 9);
1858
15
            break;
1859
13
        case 29:
1860
            // Send MID 0x64/28, to get back MID 0x64/92
1861
13
            (void)sky_write(session,
1862
13
                            "\xA0\xA1\x00\x02\x64\x28\x4c\x0d\x0a", 9);
1863
13
            break;
1864
11
        case 30:
1865
            // Send MID 0x64/30, to get back MID 0x64/98
1866
            // not on PX1172RH_DS
1867
11
            (void)sky_write(session,
1868
11
                            "\xA0\xA1\x00\x02\x64\x30\x54\x0d\x0a", 9);
1869
11
            break;
1870
11
        case 31:
1871
            // Send MID 0x64/31, to get back MID 0x64/
1872
            // not on PX1172RH_DS
1873
11
            (void)sky_write(session,
1874
11
                            "\xA0\xA1\x00\x02\x64\x31\x55\x0d\x0a", 9);
1875
11
            break;
1876
9
        case 32:
1877
            // Send MID 0x64/7d, to get back MID 0x64/fe
1878
9
            (void)sky_write(session,
1879
9
                            "\xA0\xA1\x00\x02\x64\x7d\x19\x0d\x0a", 9);
1880
9
            break;
1881
9
        case 33:
1882
            // Send MID 0x64/35, to get back MID 0x64/99
1883
            // not on PX1172RH_DS
1884
9
            (void)sky_write(session,
1885
9
                            "\xA0\xA1\x00\x03\x64\x35\x01\x50\x0d\x0a", 10);
1886
9
            break;
1887
7
        case 34:
1888
            // Send MID 0x64/36, to get back MID 0x64/9a
1889
            // not on PX1172RH_DS
1890
7
            (void)sky_write(session,
1891
7
                            "\xA0\xA1\x00\x02\x64\x36\x52\x0d\x0a", 9);
1892
7
            break;
1893
7
        case 35:
1894
            // Send MID 0x64/3c, to get back MID 0x64/99
1895
            // not on PX1172RH_DS
1896
7
            (void)sky_write(session,
1897
7
                            "\xA0\xA1\x00\x04\x64\x3c\x47\x47\x19\x0d\x0a",
1898
7
                            11);
1899
7
            break;
1900
6
        case 36:
1901
            // Send MID 0x65/02, to get back MID 0x64/80
1902
6
            (void)sky_write(session,
1903
6
                            "\xA0\xA1\x00\x02\x65\x02\x67\x0d\x0a", 9);
1904
6
            break;
1905
6
        case 37:
1906
            // Send MID 0x65/04, to get back MID 0x64/8f
1907
6
            (void)sky_write(session,
1908
6
                            "\xA0\xA1\x00\x02\x65\x04\x61\x0d\x0a", 9);
1909
6
            break;
1910
6
        case 38:
1911
            // Send MID 0x6a/02, to get back MID 0x6a/83
1912
6
            (void)sky_write(session,
1913
6
                            "\xA0\xA1\x00\x02\x6a\x02\x68\x0d\x0a", 9);
1914
6
            break;
1915
6
        case 39:
1916
            // Send MID 0x6a/07, to get back MID 0x6a/83
1917
6
            (void)sky_write(session,
1918
6
                            "\xA0\xA1\x00\x02\x6a\x07\x6d\x0d\x0a", 9);
1919
6
            break;
1920
6
        case 40:
1921
            // Send MID 0x6a/0d, to get back MID 0x6a/85
1922
6
            (void)sky_write(session,
1923
6
                            "\xA0\xA1\x00\x02\x6a\x0d\x67\x0d\x0a", 9);
1924
6
            break;
1925
4
        case 41:
1926
            // Send MID 0x6a/14, to get back MID 0x6a/86
1927
4
            (void)sky_write(session,
1928
4
                            "\xA0\xA1\x00\x02\x6a\x14\xfd\x0d\x0a", 9);
1929
4
            break;
1930
4
        case 42:
1931
            // Send MID 0x6a/16, to get back MID 0x6a/89
1932
            // not on PX1172RH_DS ?
1933
4
            (void)sky_write(session,
1934
4
                            "\xA0\xA1\x00\x02\x6a\x16\x7c\x0d\x0a", 9);
1935
4
            break;
1936
4
        case 43:
1937
            // Send MID 0x7a/0e/01, to get back MID 0x7a/0e/80
1938
            // not on PX1172RH_DS ?
1939
4
            (void)sky_write(session,
1940
4
                            "\xA0\xA1\x00\x03\x7a\x0e\x01\x75\x0d\x0a", 10);
1941
4
            break;
1942
4
        case 44:
1943
            // Send MID 0x7a/0e/02, to get back MID 0x7a/0e/81
1944
            // not on PX1172RH_DS ?
1945
4
            (void)sky_write(session,
1946
4
                            "\xA0\xA1\x00\x03\x7a\x0e\x02\x76\x0d\x0a", 10);
1947
4
            break;
1948
4
        case 45:
1949
            // Send MID 0x7a/0e/03, to get back MID 0x7a/0e/82
1950
            // not on PX1172RH_DS ?
1951
4
            (void)sky_write(session,
1952
4
                            "\xA0\xA1\x00\x03\x7a\x0e\x03\x77\x0d\x0a", 10);
1953
4
            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
2
        default:
1961
            // Done
1962
2
            session->cfg_stage = UINT_MAX;
1963
2
            break;
1964
1.54k
        }
1965
1.54k
    }
1966
1967
12.2k
    if (SKY_PACKET == session->lexer.type) {
1968
10.5k
        return  sky_parse(session, session->lexer.outbuffer,
1969
10.5k
                        session->lexer.outbuflen);
1970
10.5k
    }
1971
1.69k
    if (NMEA_PACKET == session->lexer.type) {
1972
1.69k
        return nmea_parse((char *)session->lexer.outbuffer, session);
1973
1.69k
    }
1974
    // should not get here...
1975
1976
0
    return 0;
1977
1.69k
}
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