Coverage Report

Created: 2023-11-19 07:13

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