Coverage Report

Created: 2026-08-13 07:04

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
886
#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.57k
{
58
1.57k
    uint8_t chk;
59
1.57k
    uint16_t len;
60
1.57k
    ssize_t i;
61
1.57k
    bool ok;
62
1.57k
    unsigned type = (unsigned)msg[4];
63
1.57k
    uint8_t buf[BUFSIZ];
64
1.57k
    uint8_t outbuf[BUFSIZ];
65
66
    // do not write if -b (readonly) option set
67
    // "passive" handled earlier
68
1.57k
    if (session->context->readonly) {
69
461
        return data_len;
70
461
    }
71
72
1.11k
    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.11k
    memcpy(buf, msg, data_len);
78
79
    // max length is undocumented, largest I could find is 261
80
1.11k
    len = (buf[2] << 8) | buf[3];
81
    // limit to 512 to pacify coverity
82
1.11k
    if (512 < len) {
83
0
        len = 512;
84
0
    }
85
1.11k
    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.11k
    chk = 0;
97
    // coverity_submit[tainted_data]
98
2.80k
    for (i = 0; i < len; i++) {
99
1.69k
        chk ^= buf[4 + i];
100
1.69k
    }
101
102
    // enter checksum after payload
103
1.11k
    buf[len + 4] = chk;
104
1.11k
    len += 7;
105
106
1.11k
    GPSD_LOG(LOG_IO, &session->context->errout,
107
1.11k
             "Skytraq: Writing control MID %02x: %s\n", type,
108
1.11k
             gps_hexdump((char *)outbuf, sizeof(outbuf), buf, len));
109
1.11k
    ok = gpsd_write(session, (const char *)buf, len) == len;
110
111
1.11k
    return ok;
112
1.11k
}
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.36k
{
150
    // fit into gnssid:svid
151
3.36k
    if (0 == PRN) {
152
        // skip 0 PRN
153
146
        *gnssId = 0;
154
146
        *svId = 0;
155
3.21k
    } else if ((1 <= PRN) &&
156
3.21k
               (32 >= PRN)) {
157
        // GPS
158
542
        *gnssId = 0;
159
542
        *svId = PRN;
160
2.67k
    } else if ((65 <= PRN) &&
161
2.00k
               (96 >= PRN)) {
162
        // GLONASS
163
321
        *gnssId = 6;
164
321
        *svId = PRN - 64;
165
2.35k
    } else if ((120 <= PRN) &&
166
1.34k
               (158 >= PRN)) {
167
        // SBAS
168
269
        *gnssId = 1;
169
269
        *svId = PRN;
170
2.08k
    } else if ((201 <= PRN) &&
171
835
               (239 >= PRN)) {
172
        // BeiDou
173
236
        *gnssId = 3;
174
236
        *svId = PRN - 200;
175
1.84k
    } else if ((240 <= PRN) &&
176
599
               (254 >= PRN)) {
177
        // IRNSS
178
232
        *gnssId = 20;
179
232
        *svId = PRN - 240;
180
1.61k
    } else {
181
        // huh?
182
1.61k
        *gnssId = 0;
183
1.61k
        *svId = 0;
184
1.61k
    }
185
3.36k
    return;
186
3.36k
}
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
202
{
196
202
    unsigned sid;
197
202
    unsigned u[23];
198
202
    int i;
199
200
202
    if (3 > len) {
201
124
        GPSD_LOG(LOG_WARN, &session->context->errout,
202
124
                 "Skytraq 0x62: bad len %zu\n", len);
203
124
        return 0;
204
124
    }
205
206
78
    sid = getub(buf, 1);
207
78
    switch (sid) {
208
2
    case  0x80:
209
        // SBAS status
210
2
        if (8 < len) {
211
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
212
1
                     "Skytraq 0x62/80: bad len %zu\n", len);
213
1
            return 0;
214
1
        }
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
1
    case  0x81:
224
        // QXSS status
225
1
        u[0] = getub(buf, 2);
226
1
        u[1] = getub(buf, 3);
227
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
228
1
                 "Skytraq 0x62/81: enable %u chans %u\n",
229
1
                 u[0], u[1]);
230
1
        break;
231
3
    case  0x82:
232
        // SBAS advanced status
233
3
        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
46
        for (i = 0; i < 22; i++) {
239
44
            u[i] = getub(buf, i + 2);
240
44
        }
241
2
        GPSD_LOG(LOG_PROG, &session->context->errout,
242
2
                 "Skytraq 0x62/82: enable %u ranging %u URA %u corr %u "
243
2
                 "chans %u mask x%02x WAAS %u %u %u %u "
244
2
                 "EGNOS %u %u %u %u MSAS %u %u %u %u "
245
2
                 "GAGAN %u %u %u %u\n",
246
2
                 u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8],
247
2
                 u[9], u[10], u[11], u[12], u[13], u[14], u[15], u[16], u[17],
248
2
                 u[18], u[19], u[20], u[21]);
249
2
        break;
250
72
    default:
251
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
252
78
                 "Skytraq 0x62: SID x%02x len %zu\n", sid, len);
253
78
    }
254
76
    return 0;
255
78
}
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
186
{
265
186
    unsigned sid;
266
267
186
    if (3 > len) {
268
114
        GPSD_LOG(LOG_WARN, &session->context->errout,
269
114
                 "Skytraq 0x63: bad len %zu\n", len);
270
114
        return 0;
271
114
    }
272
273
72
    sid = getub(buf, 1);
274
275
    // FIXME: decode them!
276
72
    GPSD_LOG(LOG_PROG, &session->context->errout,
277
72
             "Skytraq 0x63: SID %u\n", sid);
278
72
    return 0;
279
186
}
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
217
{
289
217
    unsigned sid;
290
217
    unsigned u[13];
291
217
    int i;
292
217
    int s[3];
293
294
217
    if (3 > len) {
295
1
        GPSD_LOG(LOG_WARN, &session->context->errout,
296
1
                 "Skytraq 0x64: bad len %zu\n", len);
297
1
        return 0;
298
1
    }
299
300
216
    sid = getub(buf, 1);
301
216
    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
2
    case  0x81:
311
        // Extended NMEA Message Interval
312
2
        if (14 > len) {
313
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
314
1
                     "Skytraq 0x64/81: bad len %zu\n", len);
315
1
            return 0;
316
1
        }
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
1
    case  0x88:
341
        // Position/Fix navigation mask
342
1
        u[0] = getub(buf, 2);
343
1
        u[1] = getub(buf, 3);
344
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
345
1
                 "Skytraq 0x64/88: 1st %u subsequent %u\n",
346
1
                 u[0], u[1]);
347
1
        break;
348
2
    case  0x8a:
349
        // GPS UTC Reference time
350
2
        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
1
        u[0] = getub(buf, 2);
356
1
        u[1] = getbeu16(buf, 3);
357
1
        u[2] = getub(buf, 5);
358
1
        u[3] = getub(buf, 6);
359
1
        GPSD_LOG(LOG_PROG, &session->context->errout,
360
1
                 "Skytraq 0x64/8a: enable %u year %u month %u day %u\n",
361
1
                 u[0], u[1], u[2], u[3]);
362
1
        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
67
    case  0x8c:
370
        // GNSS Constellation type for nav solution
371
67
        u[0] = getbeu16(buf, 2);
372
67
        GPSD_LOG(LOG_PROG, &session->context->errout,
373
67
                 "Skytraq 0x64/8c: Nav Type x%02x\n", u[0]);
374
67
        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
72
    default:
402
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
403
216
                 "Skytraq 0x64: SID x%02x len %zu\n", sid, len);
404
216
    }
405
214
    return 0;
406
216
}
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
201
{
416
201
    unsigned sid;
417
201
    unsigned u[13];
418
419
201
    if (3 > len) {
420
128
        GPSD_LOG(LOG_WARN, &session->context->errout,
421
128
                 "Skytraq 0x65: bad len %zu\n", len);
422
128
        return 0;
423
128
    }
424
425
73
    sid = getub(buf, 1);
426
73
    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
71
    default:
440
71
        GPSD_LOG(LOG_PROG, &session->context->errout,
441
73
                 "Skytraq 0x65: SID x%02x len %zu\n", sid, len);
442
73
    }
443
73
    return 0;
444
73
}
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
109
{
454
109
    unsigned sid;
455
109
    unsigned u[13];
456
109
    double d[5];
457
458
109
    if (3 > len) {
459
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
460
71
                 "Skytraq 0x6A: bad len %zu\n", len);
461
71
        return 0;
462
71
    }
463
464
38
    sid = getub(buf, 1);
465
38
    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
35
    default:
496
35
        GPSD_LOG(LOG_PROG, &session->context->errout,
497
38
                 "Skytraq 0x6A: SID x%02x len %zu\n", sid, len);
498
38
    }
499
38
    return 0;
500
38
}
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
147
{
510
147
    unsigned sid, ssid;
511
147
    unsigned u[16];
512
147
    double d[2];
513
147
    int i;
514
515
147
    if (3 > len) {
516
69
        GPSD_LOG(LOG_WARN, &session->context->errout,
517
69
                 "Skytraq 0x7A: bad len %zu\n", len);
518
69
        return 0;
519
69
    }
520
521
78
    sid = getub(buf, 1);
522
78
    ssid = getub(buf, 2);
523
78
    switch ((sid << 8) | ssid) {
524
3
    case 0x0e80:
525
        // Moving base software version
526
3
        if (16 > len) {
527
1
            GPSD_LOG(LOG_WARN, &session->context->errout,
528
1
                     "Skytraq 0x7A/0E/80: bad len %zu\n", len);
529
1
            return 0;
530
1
        }
531
28
        for (i = 0; i < 13; i++) {
532
26
            u[i] = getub(buf, i + 3);
533
26
        }
534
2
        GPSD_LOG(LOG_PROG, &session->context->errout,
535
2
                 "Skytraq 0x7A/0E/80: type %u "
536
2
                  "kver %u.%u.%u over %u.%u.%u rev %02u.%02u.%02u\n",
537
2
                 u[0], u[2], u[3], u[4], u[6], u[7], u[8], u[10],
538
2
                 u[11], u[12]);
539
2
        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
72
    default:
564
72
        GPSD_LOG(LOG_PROG, &session->context->errout,
565
78
                 "Skytraq 0x7A: SID x%02x/%02x len %zu\n", sid, ssid, len);
566
78
    }
567
77
    return 0;
568
78
}
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
148
{
582
148
    unsigned kver_x;  // kernel version
583
148
    unsigned kver_y;  // kernel version
584
148
    unsigned kver_z;  // kernel version
585
148
    unsigned over_x;  // ODM version
586
148
    unsigned over_y;  // ODM version
587
148
    unsigned over_z;  // ODM version
588
148
    unsigned rev_yy;  // revision
589
148
    unsigned rev_mm;  // revision
590
148
    unsigned rev_dd;  // revision
591
592
148
    if (14 != len) {
593
147
        return 0;
594
147
    }
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
148
}
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
74
{
626
74
    unsigned type, crc;
627
628
74
    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
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
74
}
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
142
{
650
142
    unsigned rate;
651
652
142
    if (2 != len) {
653
67
        GPSD_LOG(LOG_WARN, &session->context->errout,
654
67
                 "Skytraq 0x86: bad len %zu\n", len);
655
67
        return 0;
656
67
    }
657
658
75
    rate = getub(buf, 1);
659
660
75
    GPSD_LOG(LOG_PROG, &session->context->errout,
661
75
             "Skytraq 0x86: rate %u\n", rate);
662
75
    return 0;
663
142
}
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
78
{
701
78
    unsigned i;
702
78
    uint8_t u[15];
703
704
78
    if (16 != len) {
705
77
        GPSD_LOG(LOG_WARN, &session->context->errout,
706
77
                 "Skytraq 0x8A: bad len %zu\n", len);
707
77
        return 0;
708
77
    }
709
710
16
    for (i = 0; i < 15; i++) {
711
15
        u[i] = getub(buf, i + 1);
712
15
    }
713
1
    GPSD_LOG(LOG_PROG, &session->context->errout,
714
1
             "Skytraq 0x8A: enable %u MSM %u 1005 %u 107x %u 108x %u "
715
1
             "109x %u 110x %u 111x %u 112x %u 1019 %u 1020 %u "
716
1
             "1042 %u 1046 %u type %u version %u\n",
717
1
             u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7],
718
1
             u[8], u[9], u[10], u[11], u[12], u[13], u[14]);
719
720
1
    return 0;
721
78
}
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
70
{
731
70
    uint8_t u[5];
732
70
    double d[3];
733
734
70
    if (35 != len) {
735
69
        GPSD_LOG(LOG_WARN, &session->context->errout,
736
69
                 "Skytraq 0x8B: bad len %zu\n", len);
737
69
        return 0;
738
69
    }
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
70
}
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
142
{
765
142
    unsigned mode;
766
767
142
    if (2 != len) {
768
66
        GPSD_LOG(LOG_WARN, &session->context->errout,
769
66
                 "Skytraq 0x93: bad len %zu\n", len);
770
66
        return 0;
771
66
    }
772
773
76
    mode = getub(buf, 1);
774
775
76
    GPSD_LOG(LOG_PROG, &session->context->errout,
776
76
             "Skytraq 0x93: mode %u\n", mode);
777
76
    return 0;
778
142
}
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
77
{
796
77
    unsigned char  navmode; // Navigation fix mode (0: No fix, 1: 2D, 2: 3D, 3: 3D+DGNSS
797
77
    unsigned short week;    // GNSS week number
798
77
    double   ftow;       // Time of week
799
77
    timespec_t ts_tow;
800
77
    char ts_buf[TIMESPEC_LEN];
801
802
77
    int *mode = &session->newdata.mode;
803
77
    int *status = &session->newdata.status;
804
77
    gps_mask_t mask = 0;
805
806
77
    if (59 != len) {
807
74
        GPSD_LOG(LOG_INF, &session->context->errout, "Skytraq: "
808
74
            "Navigation Data Message has incorrect length %zu\n", len);
809
74
        return 0;
810
74
    }
811
812
3
    navmode = getub(buf, 1);
813
3
    switch (navmode) {
814
1
    case 1: // 2D fix
815
1
        *mode = MODE_2D;
816
1
        *status = STATUS_GPS;
817
1
        break;
818
1
    case 2: // 3D fix
819
1
        *mode = MODE_3D;
820
1
        *status = STATUS_GPS;
821
1
        break;
822
0
    case 3: // 3D DGPS fix
823
0
        *mode = MODE_3D;
824
0
        *status = STATUS_DGPS;
825
0
        break;
826
827
1
    default: // Includes SKY_MODE_NONE
828
1
        *mode = MODE_NO_FIX;
829
1
        *status = STATUS_UNK;
830
1
        break;
831
3
    }
832
3
    mask |= MODE_SET | STATUS_SET;
833
834
3
    session->gpsdata.satellites_used = getub(buf, 2);
835
836
3
    mask |= ONLINE_SET;
837
3
    week = getbeu16(buf,  3);
838
3
    ftow = getbeu32(buf,  5) / 100.0;
839
3
    DTOTS(&ts_tow, (int)ftow);
840
841
3
    session->newdata.time = gpsd_gpstime_resolv(session, week, ts_tow);
842
3
    mask |= TIME_SET;
843
844
3
    if (MODE_2D == *mode || MODE_3D == *mode) {
845
2
        session->newdata.latitude  = getbes32(buf,  9) / 1e7;
846
2
        session->newdata.longitude = getbes32(buf, 13) / 1e7;
847
2
        mask |= LATLON_SET | NTPTIME_IS;
848
849
2
        if (MODE_3D == *mode) {
850
1
            session->newdata.altHAE  = getbes32(buf, 17) / 100.0;
851
1
            session->newdata.altMSL  = getbes32(buf, 21) / 100.0;
852
853
1
            session->newdata.ecef.x  = getbes32(buf, 35) / 100.0;
854
1
            session->newdata.ecef.y  = getbes32(buf, 39) / 100.0;
855
1
            session->newdata.ecef.z  = getbes32(buf, 43) / 100.0;
856
1
            session->newdata.ecef.vx = getbes32(buf, 47) / 100.0;
857
1
            session->newdata.ecef.vy = getbes32(buf, 51) / 100.0;
858
1
            session->newdata.ecef.vz = getbes32(buf, 55) / 100.0;
859
1
            mask |= ECEF_SET | VECEF_SET | ALTITUDE_SET;
860
1
        }
861
2
    }
862
863
3
    session->gpsdata.dop.gdop = getbeu16(buf, 25) / 100.0;
864
3
    session->gpsdata.dop.pdop = getbeu16(buf, 27) / 100.0;
865
3
    session->gpsdata.dop.hdop = getbeu16(buf, 29) / 100.0;
866
3
    session->gpsdata.dop.vdop = getbeu16(buf, 31) / 100.0;
867
3
    session->gpsdata.dop.tdop = getbeu16(buf, 33) / 100.0;
868
3
    mask |= DOP_SET | CLEAR_IS | REPORT_IS;
869
3
    GPSD_LOG(LOG_DATA, &session->context->errout,
870
3
         "Skytraq: NAVDATA time=%s, lat=%.7f lon=%.7f altHAE=%.2f altMSL=%.2f mode=%d status=%d "
871
3
         "gdop: %.2f, hdop: %.2f, pdop: %.2f, tdop: %.2f, vdop: %.2f\n",
872
3
         timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
873
3
         session->newdata.latitude,
874
3
         session->newdata.longitude,
875
3
         session->newdata.altHAE,
876
3
         session->newdata.altMSL,
877
3
         session->newdata.mode,
878
3
         session->newdata.status,
879
3
         session->gpsdata.dop.gdop,
880
3
         session->gpsdata.dop.hdop,
881
3
         session->gpsdata.dop.pdop,
882
3
         session->gpsdata.dop.tdop,
883
3
         session->gpsdata.dop.vdop);
884
3
    return mask;
885
3
}
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
935
{
895
935
    unsigned datum;
896
897
935
    if (3 != len) {
898
852
        GPSD_LOG(LOG_WARN, &session->context->errout,
899
852
                 "Skytraq 0xAE: bad len %zu\n", len);
900
852
        return 0;
901
852
    }
902
903
83
    datum  = getbeu16(buf, 1);
904
905
83
    GPSD_LOG(LOG_PROG, &session->context->errout,
906
83
             "Skytraq 0xAE: datum %u\n", datum);
907
83
    return 0;
908
935
}
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
81
        GPSD_LOG(LOG_WARN, &session->context->errout,
922
81
                 "Skytraq 0xAF: bad len %zu\n", len);
923
81
        return 0;
924
81
    }
925
926
68
    mode = getub(buf, 1);
927
68
    pdop = getbeu16(buf, 2);
928
68
    hdop = getbeu16(buf, 4);
929
68
    gdop = getbeu16(buf, 6);
930
931
68
    GPSD_LOG(LOG_PROG, &session->context->errout,
932
68
             "Skytraq 0xAF: Masks: mode %u pdop %u hdop %u gdop %u\n",
933
68
             mode, pdop, hdop, gdop);
934
68
    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
201
{
945
201
    unsigned select, elevation, cnr;
946
947
201
    if (4 != len) {
948
200
        GPSD_LOG(LOG_WARN, &session->context->errout,
949
200
                 "Skytraq 0xB0: bad len %zu\n", len);
950
200
        return 0;
951
200
    }
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
201
}
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
39
{
971
39
    unsigned status, pspeed, pcnt, uspeed, ucnt, udist;
972
973
39
    if (12 != len) {
974
38
        GPSD_LOG(LOG_WARN, &session->context->errout,
975
38
                 "Skytraq 0xB4: bad len %zu\n", len);
976
38
        return 0;
977
38
    }
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
39
}
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
134
{
1001
134
    unsigned mode;
1002
1003
134
    if (2 != len) {
1004
68
        GPSD_LOG(LOG_WARN, &session->context->errout,
1005
68
                 "Skytraq 0xB9: bad len %zu\n", len);
1006
68
        return 0;
1007
68
    }
1008
1009
66
    mode = getub(buf, 1);
1010
1011
66
    GPSD_LOG(LOG_PROG, &session->context->errout,
1012
66
             "Skytraq 0xB9: mode %u\n", mode);
1013
66
    return 0;
1014
134
}
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
72
{
1024
72
    int delay;
1025
1026
72
    if (5 != len) {
1027
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
1028
71
                 "Skytraq 0xBB: bad len %zu\n", len);
1029
71
        return 0;
1030
71
    }
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
72
}
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
68
{
1047
68
    unsigned iod;   // Issue of data 0 - 255
1048
68
    unsigned wn;    // week number 0 - 65535
1049
68
    unsigned tow;   // receiver tow 0 - 604799999 in mS
1050
68
    unsigned mp;    // measurement period 1 - 1000 ms
1051
68
    char ts_buf[TIMESPEC_LEN];
1052
68
    timespec_t ts_tow;
1053
1054
68
    if (10 != len) {
1055
66
        return 0;
1056
66
    }
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
68
}
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
251
{
1082
251
    unsigned i;     // generic loop variable
1083
1084
    // buf[0] os the message ID
1085
    // Issue of data 0 - 255
1086
251
    unsigned iod = getub(buf, 1);
1087
    // number of measurements
1088
251
    unsigned nmeas = getub(buf, 2);
1089
1090
251
    GPSD_LOG(LOG_DATA, &session->context->errout,
1091
251
             "Skytraq 0xDD: iod %u, nmeas %u len %zd\n",
1092
251
             iod, nmeas, len);
1093
1094
251
    if (SKY_CHANNELS < nmeas) {
1095
69
        GPSD_LOG(LOG_WARN, &session->context->errout,
1096
69
                "Skytraq 0x0D: too many measurements %d\n",
1097
69
                 nmeas);
1098
69
        nmeas = SKY_CHANNELS;
1099
69
    }
1100
1101
251
    if (len < (3 + (nmeas * 23))) {
1102
82
        GPSD_LOG(LOG_ERROR, &session->context->errout,
1103
82
                 "Skytraq 0xDD: bad len %zd, s/b %d\n",
1104
82
                 len, 3 + (nmeas & 23));
1105
82
        return 0;
1106
82
    }
1107
1108
    // check IOD?
1109
169
    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
169
    memset(session->gpsdata.raw.meas, 0, sizeof(session->gpsdata.raw.meas));
1114
1115
1.18k
    for (i = 0; i < nmeas; i++) {
1116
1.01k
        const char *obs_code;
1117
1.01k
        int off = 3 + (23 * i);
1118
1119
1.01k
        uint8_t PRN = getub(buf, off + 0);
1120
        // carrier-to-noise density ratio dB-Hz
1121
1.01k
        uint8_t cno = getub(buf, off + 1);
1122
        // pseudorange in meters
1123
1.01k
        double prMes = getbed64((const char *)buf, off + 2);
1124
        // carrier phase in cycles
1125
1.01k
        double cpMes = getbed64((const char *)buf, off + 10);
1126
        // doppler in Hz, positive towards sat
1127
1.01k
        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.01k
        uint8_t trkStat = getub(buf, off + 22);
1137
1.01k
        uint8_t gnssId = 0;
1138
        // svId should always be from 0 to 99, except SBAS.
1139
1.01k
        uint8_t svId = 0;
1140
1.01k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1141
1142
1.01k
        session->gpsdata.raw.meas[i].gnssid = gnssId;
1143
1.01k
        switch (gnssId) {
1144
633
        case 0:       // GPS
1145
633
            FALLTHROUGH
1146
633
        case 5:       // QZSS
1147
633
            FALLTHROUGH
1148
734
        case 20:      // IRNSS, just guessing here
1149
734
            obs_code = "L1C";       // u-blox calls this L1C/A ?
1150
734
            break;
1151
100
        case 1:       // SBAS
1152
100
            if (100 < svId) {
1153
                // Should be always true, but pacify Coverity 498045
1154
100
                svId -= 100;            // adjust for RINEX 3 svid
1155
100
            }
1156
100
            obs_code = "L1C";       // u-blox calls this L1C/A
1157
100
            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
105
        case 6:       // GLONASS
1170
105
            obs_code = "L1C";       // u-blox calls this L1OF
1171
105
            break;
1172
1.01k
        }
1173
1.01k
        (void)strlcpy(session->gpsdata.raw.meas[i].obs_code, obs_code,
1174
1.01k
                      sizeof(session->gpsdata.raw.meas[i].obs_code));
1175
1176
1.01k
        session->gpsdata.raw.meas[i].svid = svId;
1177
1.01k
        session->gpsdata.raw.meas[i].snr = cno;
1178
1.01k
        session->gpsdata.raw.meas[i].satstat = trkStat;
1179
1.01k
        if (trkStat & 1) {
1180
            // prMes valid
1181
403
            session->gpsdata.raw.meas[i].pseudorange = prMes;
1182
616
        } else {
1183
616
            session->gpsdata.raw.meas[i].pseudorange = NAN;
1184
616
        }
1185
1.01k
        if (trkStat & 2) {
1186
            // doppler valid
1187
512
            session->gpsdata.raw.meas[i].doppler = doMes;
1188
512
        } else {
1189
507
            session->gpsdata.raw.meas[i].doppler = NAN;
1190
507
        }
1191
1.01k
        if (trkStat & 4) {
1192
            // cpMes valid
1193
475
            session->gpsdata.raw.meas[i].carrierphase = cpMes;
1194
544
        } else {
1195
544
            session->gpsdata.raw.meas[i].carrierphase = NAN;
1196
544
        }
1197
1.01k
        session->gpsdata.raw.meas[i].codephase = NAN;
1198
1.01k
        session->gpsdata.raw.meas[i].deltarange = NAN;
1199
        // skytraq does not report locktime, so assume max
1200
1.01k
        session->gpsdata.raw.meas[i].locktime = LOCKMAX;
1201
1.01k
        if (trkStat & 8) {
1202
            // possible slip
1203
428
            session->gpsdata.raw.meas[i].lli = 2;
1204
428
        }
1205
1.01k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1206
1.01k
                 "PRN %u (%u:%u) prMes %f cpMes %f doMes %f\n"
1207
1.01k
                 "cno %u  rtkStat %u\n", PRN,
1208
1.01k
                 gnssId, svId, prMes, cpMes, doMes, cno, trkStat);
1209
1210
1.01k
    }
1211
1212
    // return RAW_IS;  // WIP
1213
169
    return 0;
1214
169
}
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
478
{
1224
478
    int st, nsv;
1225
478
    unsigned i;
1226
478
    unsigned iod;   // Issue of data 0 - 255
1227
478
    unsigned nsvs;  // number of SVs in this packet
1228
1229
478
    if (3 > len) {
1230
126
        GPSD_LOG(LOG_WARN, &session->context->errout,
1231
126
                 "Skytraq 0xDE: bad len %zu\n", len);
1232
126
        return 0;
1233
126
    }
1234
352
    iod = getub(buf, 1);
1235
352
    nsvs = getub(buf, 2);
1236
    // too many sats?
1237
352
    if (SKY_CHANNELS < nsvs) {
1238
70
        GPSD_LOG(LOG_WARN, &session->context->errout,
1239
70
                "Skytraq 0xDE: too many satellites %d\n",
1240
70
                 nsvs);
1241
70
        return 0;
1242
70
    }
1243
1244
282
    if (len < (3 + (nsvs * 10))) {
1245
68
        GPSD_LOG(LOG_WARN, &session->context->errout,
1246
68
                 "Skytraq 0xDE: bad len %zu\n", len);
1247
68
        return 0;
1248
68
    }
1249
214
    gpsd_zero_satellites(&session->gpsdata);
1250
2.79k
    for (i = st = nsv =  0; i < nsvs; i++) {
1251
        // offset into buffer of start of this sat
1252
2.58k
        unsigned off = 3 + (10 * i);
1253
2.58k
        bool good;               // do we have a good record ?
1254
2.58k
        unsigned sv_stat;
1255
2.58k
        unsigned chan_stat;
1256
2.58k
        unsigned ura;
1257
2.58k
        short PRN;
1258
2.58k
        uint8_t gnssId = 0;
1259
2.58k
        uint8_t svId = 0;
1260
1261
2.58k
        PRN = (short)getub(buf, off + 1);
1262
        // fit into gnssid:svid
1263
2.58k
        if (0 == PRN) {
1264
            // skip 0 PRN
1265
241
            continue;
1266
241
        }
1267
2.34k
        PRN2_gnssId_svId(PRN, &gnssId, &svId);
1268
1269
2.34k
        session->gpsdata.skyview[st].gnssid = gnssId;
1270
2.34k
        session->gpsdata.skyview[st].svid = svId;
1271
2.34k
        session->gpsdata.skyview[st].PRN = PRN;
1272
1273
2.34k
        sv_stat = getub(buf, off + 2);
1274
2.34k
        ura = getub(buf, off + 3);
1275
2.34k
        session->gpsdata.skyview[st].ss = (double)getub(buf, off + 4);
1276
2.34k
        session->gpsdata.skyview[st].elevation =
1277
2.34k
            (double)getbes16(buf, off + 5);
1278
2.34k
        session->gpsdata.skyview[st].azimuth =
1279
2.34k
            (double)getbes16(buf, off + 7);
1280
2.34k
        chan_stat = getub(buf, off + 9);
1281
1282
2.34k
        session->gpsdata.skyview[st].used = (bool)(chan_stat & 0x30);
1283
2.34k
        good = session->gpsdata.skyview[st].PRN != 0 &&
1284
2.34k
            session->gpsdata.skyview[st].azimuth != 0 &&
1285
2.15k
            session->gpsdata.skyview[st].elevation != 0;
1286
1287
2.34k
        GPSD_LOG(LOG_DATA, &session->context->errout,
1288
2.34k
                 "Skytraq PRN=%2d El=%4.0f Az=%5.0f ss=%3.2f stat=%02x,%02x "
1289
2.34k
                 "ura=%d %c\n",
1290
2.34k
                 session->gpsdata.skyview[st].PRN,
1291
2.34k
                 session->gpsdata.skyview[st].elevation,
1292
2.34k
                 session->gpsdata.skyview[st].azimuth,
1293
2.34k
                 session->gpsdata.skyview[st].ss,
1294
2.34k
                 chan_stat, sv_stat, ura,
1295
2.34k
                 good ? '*' : ' ');
1296
1297
2.34k
        if (good) {
1298
2.07k
            if (session->gpsdata.skyview[st].used) {
1299
1.61k
                nsv++;
1300
1.61k
            }
1301
2.07k
            st += 1;
1302
2.07k
        }
1303
2.34k
    }
1304
1305
214
    session->gpsdata.satellites_visible = st;
1306
214
    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
214
    session->gpsdata.satellites_used = nsv;
1313
1314
214
    GPSD_LOG(LOG_DATA, &session->context->errout,
1315
214
             "Skytraq 0xDE: nsvs=%u visible=%u iod=%u\n", nsvs,
1316
214
             session->gpsdata.satellites_visible, iod);
1317
214
    return SATELLITE_SET | USED_IS;
1318
282
}
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
88
{
1328
88
    unsigned short navstat;
1329
88
    unsigned iod;           // Issue of data 0 - 255
1330
88
    unsigned wn;            // week number 0 - 65535
1331
88
    double f_tow;           // receiver tow Sec
1332
88
    double clock_bias;
1333
88
    double clock_drift;
1334
88
    gps_mask_t mask = 0;
1335
88
    timespec_t ts_tow;
1336
88
    char ts_buf[TIMESPEC_LEN];
1337
1338
88
    if (81 != len) {
1339
86
        return 0;
1340
86
    }
1341
1342
2
    iod = (unsigned)getub(buf, 1);
1343
1344
    // fix status is byte 2
1345
2
    navstat = (unsigned short)getub(buf, 2);
1346
2
    session->newdata.status = STATUS_UNK;
1347
2
    session->newdata.mode = MODE_NO_FIX;
1348
2
    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
1
    case 3:
1357
1
        session->newdata.status = STATUS_GPS;
1358
1
        session->newdata.mode = MODE_3D;
1359
1
        break;
1360
0
    case 4:
1361
0
        session->newdata.status = STATUS_DGPS;
1362
0
        session->newdata.mode = MODE_3D;
1363
0
        break;
1364
1
    default:
1365
1
        break;
1366
2
    }
1367
1368
2
    wn = getbeu16(buf, 3);
1369
2
    f_tow = getbed64((const char *)buf, 5);
1370
2
    DTOTS(&ts_tow, f_tow);
1371
1372
    // position/velocity is bytes 13-48, meters and m/s
1373
2
    session->newdata.ecef.x = (double)getbed64((const char *)buf, 13),
1374
2
    session->newdata.ecef.y = (double)getbed64((const char *)buf, 21),
1375
2
    session->newdata.ecef.z = (double)getbed64((const char *)buf, 29),
1376
2
    session->newdata.ecef.vx = (double)getbef32((const char *)buf, 37),
1377
2
    session->newdata.ecef.vy = (double)getbef32((const char *)buf, 41),
1378
2
    session->newdata.ecef.vz = (double)getbef32((const char *)buf, 45);
1379
2
    mask |= ECEF_SET | VECEF_SET;
1380
1381
2
    clock_bias = getbed64((const char *)buf, 49);
1382
2
    clock_drift = getbes32(buf, 57);
1383
1384
2
    session->gpsdata.dop.gdop = getbef32((const char *)buf, 61);
1385
2
    session->gpsdata.dop.pdop = getbef32((const char *)buf, 65);
1386
2
    session->gpsdata.dop.hdop = getbef32((const char *)buf, 69);
1387
2
    session->gpsdata.dop.vdop = getbef32((const char *)buf, 73);
1388
2
    session->gpsdata.dop.tdop = getbef32((const char *)buf, 77);
1389
2
    mask |= DOP_SET;
1390
1391
2
    session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow );
1392
1393
2
    GPSD_LOG(LOG_DATA, &session->context->errout,
1394
2
             "Skytraq 0xDF: iod=%u, stat=%u, wn=%u, tow=%f, t=%s "
1395
2
             "cb: %f, cd: %f "
1396
2
             "gdop: %.2f, pdop: %.2f, hdop: %.2f, vdop: %.2f, tdop: %.2f\n",
1397
2
             iod, navstat, wn, f_tow,
1398
2
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
1399
2
             clock_bias, clock_drift,
1400
2
             session->gpsdata.dop.gdop,
1401
2
             session->gpsdata.dop.pdop,
1402
2
             session->gpsdata.dop.hdop,
1403
2
             session->gpsdata.dop.vdop,
1404
2
             session->gpsdata.dop.tdop);
1405
1406
2
    mask |= TIME_SET | STATUS_SET | MODE_SET | CLEAR_IS | REPORT_IS;
1407
2
    return mask;
1408
2
}
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
203
{
1419
203
    int i;
1420
203
    unsigned prn;   // GPS sat PRN
1421
203
    unsigned subf;  // subframe 1-5
1422
    // the words are preprocessed, not raw, just the 24bits of data
1423
203
    uint32_t words[10];  // subframe 1-5
1424
1425
203
    if (33 != len) {
1426
101
        return 0;
1427
101
    }
1428
1429
102
    prn = (unsigned)getub(buf, 1);
1430
102
    subf = (unsigned)getub(buf, 2);
1431
1.12k
    for (i = 0; i < 10; i++) {
1432
1.02k
        words[i] = (uint32_t)getbeu24(buf, 3 + (i * 3));
1433
1.02k
    }
1434
1435
102
    GPSD_LOG(LOG_DATA, &session->context->errout,
1436
102
             "Skytraq 0xE0: prn=%u, subf=%u\n",
1437
102
             prn, subf);
1438
1439
    // could be SBAS?
1440
102
    return gpsd_interpret_subframe(session, GNSSID_GPS, prn, words);
1441
203
}
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
151
{
1455
151
    int i;
1456
151
    unsigned prn;   // BeidouPS sat PRN 206-214
1457
151
    unsigned subf;  // subframe 1-5
1458
    // the words are preprocessed, not raw, just the 28 bytes of data
1459
151
    uint8_t bytes[28];  // raw data
1460
1461
151
    if (31 != len) {
1462
150
        return 0;
1463
150
    }
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
151
}
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
207
{
1493
207
    int i;
1494
207
    unsigned prn;   // BeidouPS sat PRN 201-205
1495
207
    unsigned subf;  // subframe 1-5
1496
    // the words are preprocessed, not raw, just the 28 bytes of data
1497
207
    uint8_t bytes[28];  // raw data
1498
1499
207
    if (31 != len) {
1500
206
        return 0;
1501
206
    }
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
207
}
1518
1519
1520
static gps_mask_t sky_parse(struct gps_device_t * session,
1521
                            unsigned char *buf, size_t len)
1522
6.00k
{
1523
6.00k
    gps_mask_t mask = 0;
1524
1525
6.00k
    if (0 == len) {
1526
0
        return mask;
1527
0
    }
1528
1529
6.00k
    buf += 4;   // skip the leaders and length
1530
6.00k
    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.00k
    switch (buf[0]) {
1539
202
    case 0x62:
1540
202
        mask = sky_msg_62(session, buf, len);
1541
202
        break;
1542
186
    case 0x63:
1543
186
        mask = sky_msg_63(session, buf, len);
1544
186
        break;
1545
217
    case 0x64:
1546
217
        mask = sky_msg_64(session, buf, len);
1547
217
        break;
1548
201
    case 0x65:
1549
201
        mask = sky_msg_65(session, buf, len);
1550
201
        break;
1551
109
    case 0x6A:
1552
109
        mask = sky_msg_6A(session, buf, len);
1553
109
        break;
1554
147
    case 0x7A:
1555
147
        mask = sky_msg_7A(session, buf, len);
1556
147
        break;
1557
148
    case 0x80:
1558
        // 128
1559
148
        mask = sky_msg_80(session, buf, len);
1560
148
        break;
1561
74
    case 0x81:
1562
        // Software CRC
1563
74
        mask = sky_msg_81(session, buf, len);
1564
74
        break;
1565
241
    case 0x83:
1566
        // 131 - ACK
1567
241
        if (2 == len) {
1568
1
            GPSD_LOG(LOG_PROG, &session->context->errout,
1569
1
                     "Skytraq 0x83: ACK MID %#02x\n", buf[1]);
1570
240
        } else if (3 == len) {
1571
66
            GPSD_LOG(LOG_PROG, &session->context->errout,
1572
66
                     "Skytraq 0x83: ACK MID %#02x/%02x\n", buf[1], buf[2]);
1573
174
        } else if (4 <= len) {
1574
80
            GPSD_LOG(LOG_PROG, &session->context->errout,
1575
80
                     "Skytraq 0x83: ACK MID %#02x/%02x/%02x\n",
1576
80
                     buf[1], buf[2], buf[3]);
1577
94
        } else {
1578
94
            GPSD_LOG(LOG_PROG, &session->context->errout,
1579
94
                     "Skytraq 0x83: ACK\n");
1580
94
        }
1581
241
        break;
1582
268
    case 0x84:
1583
        // 132 - NACK
1584
268
        if (2 == len) {
1585
68
            GPSD_LOG(LOG_INF, &session->context->errout,
1586
68
                     "Skytraq 0x84: NACK MID %#02x\n", buf[1]);
1587
200
        } 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
199
        } else if (4 <= len) {
1591
37
            GPSD_LOG(LOG_INF, &session->context->errout,
1592
37
                     "Skytraq 0x84: NACK MID %#02x/%02x/x%02x\n",
1593
37
                     buf[1], buf[2], buf[3]);
1594
162
        } else {
1595
162
            GPSD_LOG(LOG_INF, &session->context->errout,
1596
162
                     "Skytraq 0x84: NACK\n");
1597
162
        }
1598
268
        break;
1599
142
    case 0x86:
1600
        // 134 Position Update Rate
1601
142
        mask = sky_msg_86(session, buf, len);
1602
142
        break;
1603
67
    case 0x89:
1604
67
        mask = sky_msg_89(session, buf, len);
1605
67
        break;
1606
78
    case 0x8A:
1607
78
        mask = sky_msg_8A(session, buf, len);
1608
78
        break;
1609
70
    case 0x8B:
1610
70
        mask = sky_msg_8B(session, buf, len);
1611
70
        break;
1612
142
    case 0x93:
1613
        // NMEA TALKER id
1614
142
        mask = sky_msg_93(session, buf, len);
1615
142
        break;
1616
1617
77
    case 0xA8:
1618
        // Navigation Data Message
1619
77
        mask = sky_msg_A8(session, buf, len);
1620
77
        break;
1621
1622
935
    case 0xae:
1623
        // GNSS Datum
1624
935
        mask = sky_msg_AE(session, buf, len);
1625
935
        break;
1626
1627
149
    case 0xaf:
1628
        // DOP Mask
1629
149
        mask = sky_msg_AF(session, buf, len);
1630
149
        break;
1631
1632
201
    case 0xb0:
1633
        // Elevation and CNR mask
1634
201
        mask = sky_msg_B0(session, buf, len);
1635
201
        break;
1636
1637
39
    case 0xb4:
1638
        // Position Pinning Status
1639
39
        mask = sky_msg_B4(session, buf, len);
1640
39
        break;
1641
1642
134
    case 0xb9:
1643
134
        mask = sky_msg_B9(session, buf, len);
1644
134
        break;
1645
1646
72
    case 0xbb:
1647
72
        mask = sky_msg_BB(session, buf, len);
1648
72
        break;
1649
1650
68
    case 0xDC:
1651
        // 220
1652
68
        mask = sky_msg_DC(session, buf, len);
1653
68
        break;
1654
1655
421
    case 0xDD:
1656
        // 221
1657
421
        if (3 <= len) {
1658
251
            mask = sky_msg_DD(session, buf, len);
1659
251
        }
1660
421
        break;
1661
1662
478
    case 0xDE:
1663
        // 222
1664
478
        mask = sky_msg_DE(session, buf, len);
1665
478
        break;
1666
1667
88
    case 0xDF:
1668
        // 223 - Nave status (PVT)
1669
88
        mask = sky_msg_DF(session, buf, len);
1670
88
        break;
1671
1672
203
    case 0xE0:
1673
        // 224
1674
203
        mask = sky_msg_E0(session, buf, len);
1675
203
        break;
1676
1677
151
    case 0xE2:
1678
        // 226 - Beidou2 D1 Subframe data
1679
151
        mask = sky_msg_E2(session, buf, len);
1680
151
        break;
1681
1682
207
    case 0xE3:
1683
        // 227 - Beidou2 D2 Subframe data
1684
207
        mask = sky_msg_E3(session, buf, len);
1685
207
        break;
1686
1687
66
    case 0x67:   // sub-id messages
1688
66
        FALLTHROUGH
1689
145
    case 0x6F:   // sub-id messages
1690
145
        GPSD_LOG(LOG_PROG, &session->context->errout,
1691
145
                 "Skytraq Unknown MID x%02x SID x%02x length %zd\n",
1692
145
                 buf[0], buf[1], len);
1693
145
        break;
1694
340
    default:
1695
340
        GPSD_LOG(LOG_PROG, &session->context->errout,
1696
340
                 "Skytraq Unknown MID %#02x length %zd\n",
1697
340
                 buf[0], len);
1698
340
        break;
1699
6.00k
    }
1700
6.00k
    return mask;
1701
6.00k
}
1702
1703
static gps_mask_t skybin_parse_input(struct gps_device_t *session)
1704
6.85k
{
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
6.85k
    if (UINT_MAX != session->cfg_stage) {
1712
4.70k
        session->cfg_step++;
1713
4.70k
    }
1714
1715
6.85k
    if (UINT_MAX != session->cfg_stage &&
1716
4.70k
        3 <= session->cfg_step) {
1717
        // more init to do
1718
1719
1.57k
        session->cfg_stage++;
1720
1.57k
        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.57k
        switch (session->cfg_stage) {
1728
80
        case 1:
1729
            // Send MID 0x3, to get back MID 0x81 Software CRC
1730
80
            (void)sky_write(session, "\xA0\xA1\x00\x02\x03\x00\x03\x0d\x0a",
1731
80
                            9);
1732
80
            break;
1733
72
        case 2:
1734
            // Send MID 0x10, to get back MID 0x86 (Position Update Rate)
1735
72
            (void)sky_write(session, "\xA0\xA1\x00\x01\x10\x10\x0d\x0a", 8);
1736
72
            break;
1737
73
        case 3:
1738
            // Send MID 0x15, to get back MID 0xB9 (Power Mode Status)
1739
73
            (void)sky_write(session, "\xA0\xA1\x00\x01\x15\x15\x0d\x0a", 8);
1740
73
            break;
1741
73
        case 4:
1742
            // Send MID 0x1f, to get back MID 0x89 Measurement data statuS
1743
73
            (void)sky_write(session, "\xA0\xA1\x00\x01\x1f\x1f\x0d\x0a", 8);
1744
73
            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
73
        case 6:
1750
            // Send MID 0x23, to get back MID 0x8B (Base Position)
1751
73
            (void)sky_write(session, "\xA0\xA1\x00\x01\x23\x23\x0d\x0a", 8);
1752
73
            break;
1753
69
        case 7:
1754
            // Send MID 0x2d, to get back MID 0xAE (GNSS Datum)
1755
69
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2d\x2d\x0d\x0a", 8);
1756
69
            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
60
        case 9:
1762
            // Send MID 0x2F, to get back MID 0x80 Elevation and SNR mask
1763
60
            (void)sky_write(session, "\xA0\xA1\x00\x01\x2f\x2f\x0d\x0a", 8);
1764
60
            break;
1765
53
        case 10:
1766
            // Send MID 0x3a, to get back MID 0xb4 Position Pinning
1767
53
            (void)sky_write(session, "\xA0\xA1\x00\x01\x3a\x3a\x0d\x0a", 8);
1768
53
            break;
1769
56
        case 11:
1770
            // Send MID 0x44, to get back MID 0xc2 1PPS timing
1771
            // Timing mode versions only
1772
56
            (void)sky_write(session, "\xA0\xA1\x00\x01\x44\x44\x0d\x0a", 8);
1773
56
            break;
1774
51
        case 12:
1775
            // Send MID 0x46, to get back MID 0xbb 1PPS delay
1776
51
            (void)sky_write(session, "\xA0\xA1\x00\x01\x46\x46\x0d\x0a", 8);
1777
51
            break;
1778
50
        case 13:
1779
            // Send MID 0x4f, to get back MID 0x93 NMEA talker ID
1780
50
            (void)sky_write(session, "\xA0\xA1\x00\x01\x4f\x4f\x0d\x0a", 8);
1781
50
            break;
1782
46
        case 14:
1783
            // Send MID 0x56, to get back MID 0xc3 1PPS Output Mode
1784
            // Timing mode versions only
1785
46
            (void)sky_write(session, "\xA0\xA1\x00\x01\x56\x56\x0d\x0a", 8);
1786
46
            break;
1787
44
        case 15:
1788
            // Send MID 0x62/02, to get back MID 0x62/80 SBAS status
1789
44
            (void)sky_write(session,
1790
44
                            "\xA0\xA1\x00\x02\x62\x02\x60\x0d\x0a", 9);
1791
44
            break;
1792
44
        case 16:
1793
            // Send MID 0x62/04, to get back MID 0x62/81 QZSS status
1794
44
            (void)sky_write(session,
1795
44
                            "\xA0\xA1\x00\x02\x62\x04\x66\x0d\x0a", 9);
1796
44
            break;
1797
43
        case 17:
1798
            // Send MID 0x62/06, to get back MID 0x62/82 SBAS Advanced status
1799
43
            (void)sky_write(session,
1800
43
                            "\xA0\xA1\x00\x02\x62\x06\x64\x0d\x0a", 9);
1801
43
            break;
1802
38
        case 18:
1803
            // Send MID 0x63/02, to get back MID 0x62/80 SAEE Status
1804
            // not on PX1172RH_DS
1805
38
            (void)sky_write(session,
1806
38
                            "\xA0\xA1\x00\x02\x63\x02\x61\x0d\x0a", 9);
1807
38
            break;
1808
38
        case 19:
1809
            // Send MID 0x64/01, to get back MID 0x64/80
1810
38
            (void)sky_write(session,
1811
38
                            "\xA0\xA1\x00\x02\x64\x01\x65\x0d\x0a", 9);
1812
38
            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
34
        case 21:
1819
            // Send MID 0x64/07, to get back MID 0x64/83
1820
34
            (void)sky_write(session,
1821
34
                            "\xA0\xA1\x00\x02\x64\x07\x63\x0d\x0a", 9);
1822
34
            break;
1823
32
        case 22:
1824
            // Send MID 0x64/0b, to get back MID 0x64/85
1825
32
            (void)sky_write(session,
1826
32
                            "\xA0\xA1\x00\x02\x64\x0b\x6f\x0d\x0a", 9);
1827
32
            break;
1828
31
        case 23:
1829
            // Send MID 0x64/12, to get back MID 0x64/88
1830
31
            (void)sky_write(session,
1831
31
                            "\xA0\xA1\x00\x02\x64\x12\x76\x0d\x0a", 9);
1832
31
            break;
1833
29
        case 24:
1834
            // Send MID 0x64/16, to get back MID 0x64/8a
1835
29
            (void)sky_write(session,
1836
29
                            "\xA0\xA1\x00\x02\x64\x16\x72\x0d\x0a", 9);
1837
29
            break;
1838
28
        case 25:
1839
            // Send MID 0x64/18, to get back MID 0x64/8b
1840
28
            (void)sky_write(session,
1841
28
                            "\xA0\xA1\x00\x02\x64\x18\x7c\x0d\x0a", 9);
1842
28
            break;
1843
24
        case 26:
1844
            // Send MID 0x64/1a, to get back MID 0x64/8c
1845
24
            (void)sky_write(session,
1846
24
                            "\xA0\xA1\x00\x02\x64\x1a\x7e\x0d\x0a", 9);
1847
24
            break;
1848
21
        case 27:
1849
            // Send MID 0x64/20, to get back MID 0x64/8e
1850
21
            (void)sky_write(session,
1851
21
                            "\xA0\xA1\x00\x02\x64\x20\x44\x0d\x0a", 9);
1852
21
            break;
1853
20
        case 28:
1854
            // Send MID 0x64/22, to get back MID 0x64/8f
1855
            // not on PX1172RH_DS
1856
20
            (void)sky_write(session,
1857
20
                            "\xA0\xA1\x00\x02\x64\x22\x46\x0d\x0a", 9);
1858
20
            break;
1859
20
        case 29:
1860
            // Send MID 0x64/28, to get back MID 0x64/92
1861
20
            (void)sky_write(session,
1862
20
                            "\xA0\xA1\x00\x02\x64\x28\x4c\x0d\x0a", 9);
1863
20
            break;
1864
20
        case 30:
1865
            // Send MID 0x64/30, to get back MID 0x64/98
1866
            // not on PX1172RH_DS
1867
20
            (void)sky_write(session,
1868
20
                            "\xA0\xA1\x00\x02\x64\x30\x54\x0d\x0a", 9);
1869
20
            break;
1870
20
        case 31:
1871
            // Send MID 0x64/31, to get back MID 0x64/
1872
            // not on PX1172RH_DS
1873
20
            (void)sky_write(session,
1874
20
                            "\xA0\xA1\x00\x02\x64\x31\x55\x0d\x0a", 9);
1875
20
            break;
1876
19
        case 32:
1877
            // Send MID 0x64/7d, to get back MID 0x64/fe
1878
19
            (void)sky_write(session,
1879
19
                            "\xA0\xA1\x00\x02\x64\x7d\x19\x0d\x0a", 9);
1880
19
            break;
1881
17
        case 33:
1882
            // Send MID 0x64/35, to get back MID 0x64/99
1883
            // not on PX1172RH_DS
1884
17
            (void)sky_write(session,
1885
17
                            "\xA0\xA1\x00\x03\x64\x35\x01\x50\x0d\x0a", 10);
1886
17
            break;
1887
15
        case 34:
1888
            // Send MID 0x64/36, to get back MID 0x64/9a
1889
            // not on PX1172RH_DS
1890
15
            (void)sky_write(session,
1891
15
                            "\xA0\xA1\x00\x02\x64\x36\x52\x0d\x0a", 9);
1892
15
            break;
1893
15
        case 35:
1894
            // Send MID 0x64/3c, to get back MID 0x64/99
1895
            // not on PX1172RH_DS
1896
15
            (void)sky_write(session,
1897
15
                            "\xA0\xA1\x00\x04\x64\x3c\x47\x47\x19\x0d\x0a",
1898
15
                            11);
1899
15
            break;
1900
13
        case 36:
1901
            // Send MID 0x65/02, to get back MID 0x64/80
1902
13
            (void)sky_write(session,
1903
13
                            "\xA0\xA1\x00\x02\x65\x02\x67\x0d\x0a", 9);
1904
13
            break;
1905
12
        case 37:
1906
            // Send MID 0x65/04, to get back MID 0x64/8f
1907
12
            (void)sky_write(session,
1908
12
                            "\xA0\xA1\x00\x02\x65\x04\x61\x0d\x0a", 9);
1909
12
            break;
1910
10
        case 38:
1911
            // Send MID 0x6a/02, to get back MID 0x6a/83
1912
10
            (void)sky_write(session,
1913
10
                            "\xA0\xA1\x00\x02\x6a\x02\x68\x0d\x0a", 9);
1914
10
            break;
1915
10
        case 39:
1916
            // Send MID 0x6a/07, to get back MID 0x6a/83
1917
10
            (void)sky_write(session,
1918
10
                            "\xA0\xA1\x00\x02\x6a\x07\x6d\x0d\x0a", 9);
1919
10
            break;
1920
8
        case 40:
1921
            // Send MID 0x6a/0d, to get back MID 0x6a/85
1922
8
            (void)sky_write(session,
1923
8
                            "\xA0\xA1\x00\x02\x6a\x0d\x67\x0d\x0a", 9);
1924
8
            break;
1925
8
        case 41:
1926
            // Send MID 0x6a/14, to get back MID 0x6a/86
1927
8
            (void)sky_write(session,
1928
8
                            "\xA0\xA1\x00\x02\x6a\x14\xfd\x0d\x0a", 9);
1929
8
            break;
1930
7
        case 42:
1931
            // Send MID 0x6a/16, to get back MID 0x6a/89
1932
            // not on PX1172RH_DS ?
1933
7
            (void)sky_write(session,
1934
7
                            "\xA0\xA1\x00\x02\x6a\x16\x7c\x0d\x0a", 9);
1935
7
            break;
1936
7
        case 43:
1937
            // Send MID 0x7a/0e/01, to get back MID 0x7a/0e/80
1938
            // not on PX1172RH_DS ?
1939
7
            (void)sky_write(session,
1940
7
                            "\xA0\xA1\x00\x03\x7a\x0e\x01\x75\x0d\x0a", 10);
1941
7
            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
5
        case 46:
1955
            // Send MID 0x7a/0e/05, to get back MID 0x7a/0e/83
1956
            // not on PX1172RH_DS ?
1957
5
            (void)sky_write(session,
1958
5
                            "\xA0\xA1\x00\x03\x7a\x0e\x05\x71\x0d\x0a", 10);
1959
5
            break;
1960
5
        default:
1961
            // Done
1962
5
            session->cfg_stage = UINT_MAX;
1963
5
            break;
1964
1.57k
        }
1965
1.57k
    }
1966
1967
6.85k
    if (SKY_PACKET == session->lexer.type) {
1968
6.00k
        return  sky_parse(session, session->lexer.outbuffer,
1969
6.00k
                        session->lexer.outbuflen);
1970
6.00k
    }
1971
855
    if (NMEA_PACKET == session->lexer.type) {
1972
855
        return nmea_parse((char *)session->lexer.outbuffer, session);
1973
855
    }
1974
    // should not get here...
1975
1976
0
    return 0;
1977
855
}
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