Coverage Report

Created: 2026-01-09 07:09

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