Coverage Report

Created: 2025-08-24 06:44

/src/gpsd/gpsd-3.26.2~dev/drivers/driver_superstar2.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * This file is Copyright 2010 by the GPSD project
3
 * SPDX-License-Identifier: BSD-2-clause
4
 */
5
#include "../include/gpsd_config.h"  /* must be before all includes */
6
7
#include <math.h>
8
#include <stdbool.h>
9
#include <stdio.h>
10
#include <stdlib.h>
11
#include <string.h>
12
#include <time.h>
13
14
#include "../include/gpsd.h"
15
16
#if defined(SUPERSTAR2_ENABLE)
17
#include "../include/bits.h"
18
#include "../include/driver_superstar2.h"
19
#include "../include/timespec.h"
20
21
/*
22
 * These routines are specific to this driver
23
 */
24
25
static gps_mask_t superstar2_parse_input(struct gps_device_t *);
26
static gps_mask_t superstar2_dispatch(struct gps_device_t *,
27
                                      unsigned char *, size_t);
28
static gps_mask_t superstar2_msg_ack(struct gps_device_t *,
29
                                     unsigned char *, size_t);
30
static gps_mask_t superstar2_msg_navsol_lla(struct gps_device_t *,
31
                                            unsigned char *, size_t);
32
static gps_mask_t superstar2_msg_timing(struct gps_device_t *,
33
                                        unsigned char *, size_t);
34
static gps_mask_t superstar2_msg_svinfo(struct gps_device_t *,
35
                                        unsigned char *, size_t);
36
static gps_mask_t superstar2_msg_iono_utc(struct gps_device_t *,
37
                                          unsigned char *, size_t);
38
static gps_mask_t superstar2_msg_ephemeris(struct gps_device_t *,
39
                                           unsigned char *, size_t);
40
41
/*
42
 * These methods may be called elsewhere in gpsd
43
 */
44
static ssize_t superstar2_control_send(struct gps_device_t *, char *, size_t);
45
static void superstar2_event_hook(struct gps_device_t *, event_t);
46
static ssize_t superstar2_write(struct gps_device_t *, char *, size_t);
47
static bool superstar2_set_speed(struct gps_device_t *, speed_t, char, int);
48
static void superstar2_set_mode(struct gps_device_t *, int);
49
50
51
/*
52
 * Decode the message ACK message
53
 */
54
static gps_mask_t superstar2_msg_ack(struct gps_device_t *session,
55
                                     unsigned char *buf, size_t data_len)
56
0
{
57
0
    if (data_len == 11)
58
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
59
0
                 "superstar2 #126 - ACK %d %d %d %d %d\n",
60
0
                 buf[5], buf[6], buf[7], buf[8], buf[9]);
61
0
    return 0;
62
0
}
63
64
/*
65
 * Decode the navigation solution message. The ECEF version is intentionally
66
 * unhandled. By suppressing evaluation of it, we gain the desirable feature
67
 * that the fix update is atomic and exactly once per cycle.
68
 */
69
70
71
/* Navigation Data (User Coordinates) ID# 20 */
72
static gps_mask_t superstar2_msg_navsol_lla(struct gps_device_t *session,
73
                                           unsigned char *buf, size_t data_len)
74
0
{
75
0
    gps_mask_t mask;
76
0
    unsigned char flags;
77
0
    double d;
78
0
    struct tm tm = {0};
79
0
    double int_part;
80
0
    char ts_buf[TIMESPEC_LEN];
81
82
0
    if (data_len != 77)
83
0
        return 0;
84
85
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
86
0
             "superstar2 #20 - user navigation data\n");
87
0
    mask = 0;
88
89
0
    flags = (unsigned char)getub(buf, 72);
90
0
    if ((flags & 0x0f) != 0x03) /* mode 3 is navigation */
91
0
        return mask;
92
93
    /* extract time data */
94
0
    (void)memset(&tm, '\0', sizeof(tm));
95
0
    tm.tm_hour = (int)getub(buf, 4) & 0x1f;
96
0
    tm.tm_min = (int)getub(buf, 5);
97
0
    d = getled64((char *)buf, 6);           /* seconds as a double */
98
0
    session->newdata.time.tv_nsec = (long)(modf(d, &int_part) * 1e9);
99
0
    tm.tm_sec = (int)int_part;
100
0
    tm.tm_mday = (int)getub(buf, 14);
101
0
    tm.tm_mon = (int)getub(buf, 15) - 1;
102
0
    tm.tm_year = (int)getleu16(buf, 16) - 1900;
103
0
    tm.tm_isdst = 0;
104
0
    session->newdata.time.tv_sec = mkgmtime(&tm);
105
0
    mask |= TIME_SET | NTPTIME_IS;
106
107
    /* extract the local tangential plane (ENU) solution */
108
0
    session->newdata.latitude = getled64((char *)buf, 18) * RAD_2_DEG;
109
0
    session->newdata.longitude = getled64((char *)buf, 26) * RAD_2_DEG;
110
    /* unclear if this is MSL or WGS84.  Assume WGS84 */
111
0
    session->newdata.altHAE = getlef32((char *)buf, 34);
112
0
    session->newdata.speed = getlef32((char *)buf, 38);
113
0
    session->newdata.track = getlef32((char *)buf, 42) * RAD_2_DEG;
114
0
    session->newdata.climb = getlef32((char *)buf, 54);
115
0
    mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET | CLIMB_SET;
116
117
0
    session->gpsdata.satellites_used = (int)getub(buf, 71) & 0x0f;
118
0
    session->gpsdata.dop.hdop = getleu16(buf, 66) * 0.1;
119
0
    session->gpsdata.dop.vdop = getleu16(buf, 68) * 0.1;
120
    /* other DOP if available */
121
0
    mask |= DOP_SET | USED_IS;
122
123
0
    flags = (unsigned char)getub(buf, 70);
124
0
    switch (flags & 0x1f) {
125
0
    case 2:
126
0
        session->newdata.mode = MODE_3D;
127
0
        session->newdata.status = STATUS_GPS;
128
0
        break;
129
0
    case 4:
130
0
        session->newdata.mode = MODE_3D;
131
0
        session->newdata.status = STATUS_DGPS;
132
0
        break;
133
0
    case 5:
134
0
        session->newdata.mode = MODE_2D;
135
0
        session->newdata.status = STATUS_DGPS;
136
0
        break;
137
0
    case 3:
138
0
    case 6:
139
0
        session->newdata.mode = MODE_2D;
140
0
        session->newdata.status = STATUS_GPS;
141
0
        break;
142
0
    default:
143
0
        session->newdata.status = STATUS_UNK;
144
0
        session->newdata.mode = MODE_NO_FIX;
145
0
    }
146
147
0
    mask |= MODE_SET | STATUS_SET;
148
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
149
0
             "NAVSOL_LLA: time=%s lat=%.2f lon=%.2f altHAE=%.2f "
150
0
             "track=%.2f speed=%.2f climb=%.2f mode=%d status=%d hdop=%.2f "
151
0
             "hdop=%.2f used=%d\n",
152
0
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
153
0
             session->newdata.latitude,
154
0
             session->newdata.longitude,
155
0
             session->newdata.altHAE,
156
0
             session->newdata.track,
157
0
             session->newdata.speed,
158
0
             session->newdata.climb,
159
0
             session->newdata.mode,
160
0
             session->newdata.status,
161
0
             session->gpsdata.dop.hdop,
162
0
             session->gpsdata.dop.vdop,
163
0
             session->gpsdata.satellites_used);
164
0
    return mask;
165
0
}
166
167
/*
168
 * GPS Satellite Info
169
 */
170
static gps_mask_t
171
superstar2_msg_svinfo(struct gps_device_t *session,
172
                      unsigned char *buf, size_t data_len)
173
0
{
174
0
    int i, st, nchan, nsv;
175
176
0
    if (data_len != 67)
177
0
        return 0;
178
179
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
180
0
             "superstar2 #33 - satellite data\n");
181
182
0
    nchan = 12;
183
0
    gpsd_zero_satellites(&session->gpsdata);
184
0
    nsv = 0;                    /* number of actually used satellites */
185
0
    for (i = st = 0; i < nchan; i++) {
186
        /* get info for one channel/satellite */
187
0
        int off = i * 5 + 5;
188
0
        unsigned int porn;
189
0
        bool used = (getub(buf, off) & 0x60) == 0x60;
190
0
        if ((porn = (unsigned int)getub(buf, off) & 0x1f) == 0)
191
0
            porn = (unsigned int)(getub(buf, off + 3) >> 1) + 87;
192
193
0
        session->gpsdata.skyview[i].PRN = (short)porn;
194
0
        session->gpsdata.skyview[i].ss = (double)getub(buf, off + 4);
195
0
        session->gpsdata.skyview[i].elevation = (double)getsb(buf, off + 1);
196
0
        session->gpsdata.skyview[i].azimuth = (double)getub(buf, off + 2) +
197
0
            (short)((unsigned short)(getub(buf, off + 3) & 0x1) << 1);
198
0
        session->gpsdata.skyview[i].used = used;
199
0
        if (used)
200
0
            nsv++;
201
202
0
        if (session->gpsdata.skyview[i].PRN)
203
0
            st++;
204
0
    }
205
0
    session->gpsdata.skyview_time.tv_sec = 0;
206
0
    session->gpsdata.skyview_time.tv_nsec = 0;
207
0
    session->gpsdata.satellites_used = nsv;
208
0
    session->gpsdata.satellites_visible = st;
209
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
210
0
             "SVINFO: visible=%d used=%d mask={SATELLITE|USED}\n",
211
0
             session->gpsdata.satellites_visible,
212
0
             session->gpsdata.satellites_used);
213
0
    return SATELLITE_SET | USED_IS;
214
0
}
215
216
static gps_mask_t
217
superstar2_msg_version(struct gps_device_t *session,
218
                       unsigned char *buf, size_t data_len)
219
0
{
220
0
#define SZ 16
221
0
    char main_sw[SZ], hw_part[SZ], boot_sw[SZ], ser_num[SZ];
222
223
    /* byte 98 is device type, value = 3 means superstar2 */
224
0
    if ((data_len != 101) || ((getub(buf, 98) & 0x0f) != 3))
225
0
        return 0;
226
227
0
    (void)snprintf(main_sw, 15, "%s", (char *)buf + 4);
228
0
    (void)snprintf(hw_part, 15, "%s", (char *)buf + 18);
229
0
    (void)snprintf(boot_sw, 15, "%s", (char *)buf + 36);
230
0
    (void)snprintf(ser_num, 14, "%s", (char *)buf + 73);
231
232
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
233
0
             "superstar2 #45 - hw part %s boot sw %s main sw %s ser num %s\n",
234
0
             hw_part, boot_sw, main_sw, ser_num);
235
0
    (void)strlcpy(session->subtype, main_sw, sizeof(session->subtype));
236
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
237
0
             "VERSION: subtype='%s' mask={DEVEICEID}\n",
238
0
             session->subtype);
239
0
    return DEVICEID_SET;
240
0
}
241
242
/**
243
 * Timing Status ID# 113
244
 *
245
 * Precise Timing models only
246
 */
247
static gps_mask_t
248
superstar2_msg_timing(struct gps_device_t *session, unsigned char *buf,
249
                      size_t data_len)
250
0
{
251
0
    gps_mask_t mask;
252
0
    struct tm tm = {0};
253
254
0
    if (data_len != 65)
255
0
        return 0;
256
257
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
258
0
             "superstar2 #113 - timing status\n");
259
0
    if ((getub(buf, 55) & 0x30) != 0)
260
0
        mask = 0;
261
0
    else {
262
0
        double d;
263
        /* extract time data */
264
0
        (void)memset(&tm, '\0', sizeof(tm));
265
0
        tm.tm_mday = (int)getsb(buf, 37);
266
0
        tm.tm_mon = (int)getsb(buf, 38) - 1;
267
0
        tm.tm_year = (int)getles16(buf, 39) - 1900;
268
269
0
        tm.tm_hour = (int)getsb(buf, 41);
270
0
        tm.tm_min = (int)getsb(buf, 42);
271
0
        d = getled64((char *)buf, 43);
272
0
        tm.tm_sec = (int)d;
273
0
        tm.tm_isdst = 0;
274
0
        session->newdata.time.tv_sec = mkgmtime(&tm);
275
0
        session->newdata.time.tv_nsec = 0;
276
0
        session->context->leap_seconds = (int)getsb(buf, 20);
277
0
        mask = TIME_SET | NTPTIME_IS;
278
0
    }
279
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
280
0
             "TIMING: time=%lld mask={TIME}\n",
281
0
             (long long)session->newdata.time.tv_sec);
282
0
    return mask;
283
0
}
284
285
/**
286
 * Raw Measurements
287
 */
288
static gps_mask_t
289
superstar2_msg_measurement(struct gps_device_t *session, unsigned char *buf,
290
                           size_t data_len UNUSED)
291
0
{
292
0
    gps_mask_t mask = 0;
293
0
    int i, n;
294
0
    double t;
295
296
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
297
0
             "superstar2 #23 - measurement block\n");
298
299
0
    n = (int)getub(buf, 6);     /* number of measurements */
300
0
    if ((n < 1) || (n > MAXCHANNELS)) {
301
0
        GPSD_LOG(LOG_INF, &session->context->errout,
302
0
                 "too many measurements\n");
303
0
        return 0;
304
0
    }
305
0
    t = getled64((char *)buf, 7);               /* measurement time */
306
0
    DTOTS(&session->gpsdata.raw.mtime, t);
307
308
    /* this is so we can tell which never got set */
309
0
    for (i = 0; i < MAXCHANNELS; i++)
310
0
        session->gpsdata.raw.meas[i].svid = 0;
311
0
    for (i = 0; i < n; i++) {
312
0
        unsigned long ul;
313
0
        session->gpsdata.skyview[i].PRN =
314
0
            (short)(getub(buf, 11 * i + 15) & 0x1f);
315
0
        session->gpsdata.skyview[i].ss =
316
0
            (double)getub(buf, 11 * i * 15 + 1) / 4.0;
317
0
        session->gpsdata.raw.meas[i].codephase =
318
0
            (double)getleu32(buf, 11 * i * 15 + 2);
319
0
        ul = (unsigned long)getleu32(buf, 11 * i * 15 + 6);
320
321
0
        session->gpsdata.raw.meas[i].satstat = (unsigned int)(ul & 0x03L);
322
0
        session->gpsdata.raw.meas[i].carrierphase =
323
0
            (double)((ul >> 2) & 0x03ffL);
324
0
        session->gpsdata.raw.meas[i].pseudorange = (double)(ul >> 12);
325
0
    }
326
327
    /*The above decode does not look correct, do not report */
328
    /* mask |= RAW_IS; */
329
0
    return mask;
330
0
}
331
332
/* request for ionospheric and utc time data #75 */
333
static unsigned char iono_utc_msg[] = { 0x01, 0x4b, 0xb4, 0x00, 0x00, 0x01 };
334
335
336
337
/**
338
 * Ionospheric/UTC parameters
339
 */
340
static gps_mask_t
341
superstar2_msg_iono_utc(struct gps_device_t *session, unsigned char *buf,
342
                        size_t data_len UNUSED)
343
0
{
344
0
    unsigned int i, u;
345
346
0
    i = (unsigned int)getub(buf, 12);
347
0
    u = (unsigned int)getub(buf, 21);
348
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
349
0
             "superstar2 #75 - ionospheric & utc data: iono %s utc %s\n",
350
0
             i ? "ok" : "bad", u ? "ok" : "bad");
351
0
    session->driver.superstar2.last_iono = time(NULL);
352
353
0
    return 0;
354
0
}
355
356
357
/**
358
 * Ephemeris
359
 */
360
static gps_mask_t
361
superstar2_msg_ephemeris(struct gps_device_t *session, unsigned char *buf,
362
                         size_t data_len UNUSED)
363
0
{
364
0
    unsigned int prn;
365
0
    prn = (unsigned int)(getub(buf, 4) & 0x1f);
366
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
367
0
             "superstar2 #22 - ephemeris data - prn %u\n", prn);
368
369
    /* ephemeris data updates fairly slowly, but when it does, poll UTC */
370
0
    if ((time(NULL) - session->driver.superstar2.last_iono) > 60)
371
0
        (void)superstar2_write(session, (char *)iono_utc_msg,
372
0
                               sizeof(iono_utc_msg));
373
374
0
    return ONLINE_SET;
375
0
}
376
377
378
static ssize_t
379
superstar2_write(struct gps_device_t *session, char *msg, size_t msglen)
380
0
{
381
0
    unsigned short c = 0;
382
0
    ssize_t i;
383
384
0
    for (i = 0; i < (ssize_t) (msglen - 2); i++)
385
0
        c += (unsigned short)msg[i];
386
0
    c += 0x100;
387
0
    msg[(int)msg[3] + 4] = (char)((c >> 8) & 0xff);
388
0
    msg[(int)msg[3] + 5] = (char)(c & 0xff);
389
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
390
0
             "writing superstar2 control type %d len %zu\n",
391
0
             (int)msg[1] & 0x7f, msglen);
392
0
    return gpsd_write(session, msg, msglen);
393
0
}
394
395
/**
396
 * Parse the data from the device
397
 */
398
gps_mask_t
399
superstar2_dispatch(struct gps_device_t * session, unsigned char *buf,
400
                    size_t len)
401
0
{
402
0
    int type;
403
404
0
    if (len == 0)
405
0
        return 0;
406
407
0
    type = (int)buf[SUPERSTAR2_TYPE_OFFSET];
408
0
    session->cycle_end_reliable = true;
409
410
0
    switch (type) {
411
0
    case SUPERSTAR2_ACK:        /* Message Acknowledgement */
412
0
        return superstar2_msg_ack(session, buf, len);
413
0
    case SUPERSTAR2_SVINFO:     /* Satellite Visibility Data */
414
0
        return superstar2_msg_svinfo(session, buf, len);
415
0
    case SUPERSTAR2_NAVSOL_LLA: /* Navigation Data */
416
0
        return superstar2_msg_navsol_lla(session, buf,
417
0
                                         len) | (CLEAR_IS | REPORT_IS);
418
0
    case SUPERSTAR2_VERSION:    /* Hardware/Software Version */
419
0
        return superstar2_msg_version(session, buf, len);
420
0
    case SUPERSTAR2_TIMING:     /* Timing Parameters */
421
0
        return superstar2_msg_timing(session, buf, len);
422
0
    case SUPERSTAR2_MEASUREMENT:        /* Timing Parameters */
423
0
        return superstar2_msg_measurement(session, buf, len);
424
0
    case SUPERSTAR2_IONO_UTC:
425
0
        return superstar2_msg_iono_utc(session, buf, len);
426
0
    case SUPERSTAR2_EPHEMERIS:
427
0
        return superstar2_msg_ephemeris(session, buf, len);
428
429
0
    default:
430
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
431
0
                 "unknown superstar2 packet id 0x%02x length %zd\n",
432
0
                 type, len);
433
0
        return 0;
434
0
    }
435
0
}
436
437
/**********************************************************
438
 *
439
 * Externally called routines below here
440
 *
441
 **********************************************************/
442
443
static void superstar2_event_hook(struct gps_device_t *session, event_t event)
444
0
{
445
0
    if (session->context->readonly ||
446
0
        session->context->passive) {
447
0
        return;
448
0
    }
449
450
0
    if (event == EVENT_IDENTIFIED) {
451
0
        unsigned char version_msg[]    = { 0x01, 0x2d, 0xd2, 0x00, 0x00, 0x01 };
452
0
        unsigned char svinfo_msg[]     = { 0x01, 0xa1, 0x5e, 0x00, 0x00, 0x01 };
453
0
        unsigned char timing_msg[]     = { 0x01, 0xf1, 0x0e, 0x00, 0x00, 0x01 };
454
0
        unsigned char navsol_lla_msg[] = { 0x01, 0x94, 0x6b, 0x00, 0x00, 0x01 };
455
0
        unsigned char ephemeris_msg[]  = { 0x01, 0x96, 0x69, 0x00, 0x00, 0x01 };
456
0
        unsigned char measurement_msg[] =
457
0
            { 0x01, 0x97, 0x68, 0x01, 0x00, 0x01, 0x01 };
458
459
0
        (void)superstar2_write(session, (char *)timing_msg,
460
0
                               sizeof(timing_msg));
461
0
        (void)superstar2_write(session, (char *)measurement_msg,
462
0
                               sizeof(measurement_msg));
463
0
        (void)superstar2_write(session, (char *)svinfo_msg,
464
0
                               sizeof(svinfo_msg));
465
0
        (void)superstar2_write(session, (char *)navsol_lla_msg,
466
0
                               sizeof(navsol_lla_msg));
467
0
        (void)superstar2_write(session, (char *)version_msg,
468
0
                               sizeof(version_msg));
469
0
        (void)superstar2_write(session, (char *)ephemeris_msg,
470
0
                               sizeof(ephemeris_msg));
471
0
        (void)superstar2_write(session, (char *)iono_utc_msg,
472
0
                               sizeof(iono_utc_msg));
473
0
        session->driver.superstar2.last_iono = time(NULL);
474
0
    }
475
0
}
476
477
/*
478
 * This is the entry point to the driver. When the packet sniffer recognizes
479
 * a packet for this driver, it calls this method which passes the packet to
480
 * the binary processor or the nmea processor, depending on the session type.
481
 */
482
static gps_mask_t superstar2_parse_input(struct gps_device_t *session)
483
0
{
484
0
    if (SUPERSTAR2_PACKET == session->lexer.type) {
485
0
        return superstar2_dispatch(session, session->lexer.outbuffer,
486
0
                                   session->lexer.length);
487
0
    }
488
0
    if (NMEA_PACKET == session->lexer.type) {
489
0
        return nmea_parse((char *)session->lexer.outbuffer, session);
490
0
    }
491
0
    return 0;
492
0
}
493
494
static ssize_t superstar2_control_send(struct gps_device_t *session, char *msg,
495
                                       size_t msglen)
496
0
{
497
0
    session->msgbuf[0] = 0x1;      // SOH
498
0
    session->msgbuf[1] = msg[0];
499
0
    session->msgbuf[2] = msg[0] ^ 0xff;
500
0
    session->msgbuf[3] = (char)(msglen + 1);
501
0
    (void)memcpy(session->msgbuf + 4, msg + 1, msglen - 1);
502
0
    session->msgbuflen = (size_t) (msglen + 5);
503
0
    return superstar2_write(session, session->msgbuf, session->msgbuflen);
504
0
}
505
506
static bool superstar2_set_speed(struct gps_device_t *session,
507
                                 speed_t speed, char parity, int stopbits)
508
0
{
509
    /* parity and stopbit switching aren't available on this chip */
510
0
    if (parity != session->gpsdata.dev.parity
511
0
        || stopbits != (int)session->gpsdata.dev.stopbits) {
512
0
        return false;
513
0
    } else {
514
0
        unsigned char speed_msg[] =
515
0
            { 0x01, 0x48, 0xB7, 0x01, 0x00, 0x00, 0x00 };
516
517
        /* high bit 0 in the mode word means set NMEA mode */
518
0
        speed_msg[4] = (unsigned char)(speed / 300);
519
0
        return (superstar2_write(session, (char *)speed_msg, 7) == 7);
520
0
    }
521
0
}
522
523
static void superstar2_set_mode(struct gps_device_t *session, int mode)
524
0
{
525
0
    if (mode == MODE_NMEA) {
526
0
        unsigned char mode_msg[] =
527
0
            { 0x01, 0x48, 0xB7, 0x01, 0x00, 0x00, 0x00 };
528
529
        /* high bit 0 in the mode word means set NMEA mode */
530
0
        mode_msg[4] = (unsigned char)(session->gpsdata.dev.baudrate / 300);
531
0
        (void)superstar2_write(session, (char *)mode_msg, 7);
532
0
    }
533
0
}
534
535
/* *INDENT-OFF* */
536
const struct gps_type_t driver_superstar2 = {
537
    /* Full name of type */
538
    .type_name          = "SuperStarII",
539
    /* Associated lexer packet type */
540
    .packet_type        = SUPERSTAR2_PACKET,
541
    /* Driver type flags */
542
    .flags               = DRIVER_STICKY,
543
    /* Response string that identifies device (not active) */
544
    .trigger            = NULL,
545
    /* Number of satellite channels supported by the device */
546
    .channels           = 12,
547
    /* Startup-time device detector */
548
    .probe_detect       = NULL,
549
    /* Packet getter (using default routine) */
550
    .get_packet         = packet_get1,
551
    /* Parse message packets */
552
    .parse_packet       = superstar2_parse_input,
553
    /* RTCM handler (using default routine) */
554
    .rtcm_writer        = gpsd_write,
555
    /* non-perturbing initial query */
556
    .init_query         = NULL,
557
    /* Fire on various lifetime events */
558
    .event_hook         = superstar2_event_hook,
559
    /* Speed (baudrate) switch */
560
    .speed_switcher     = superstar2_set_speed,
561
    /* Switch to NMEA mode */
562
    .mode_switcher      = superstar2_set_mode,
563
    /* Message delivery rate switcher (not active) */
564
    .rate_switcher      = NULL,
565
    /* Minimum cycle time (not used) */
566
    .min_cycle.tv_sec  = 1,             /* not relevant, no rate switch */
567
    .min_cycle.tv_nsec = 0,             /* not relevant, no rate switch */
568
    /* Control string sender - should provide checksum and trailer */
569
    .control_send       = superstar2_control_send,
570
    .time_offset     = NULL,            /* no method for NTP fudge factor */
571
};
572
/* *INDENT-ON* */
573
#endif  // defined(SUPERSTAR2_ENABLE)
574
575
// vim: set expandtab shiftwidth=4