Coverage Report

Created: 2024-02-25 06:36

/src/gpsd/gpsd-3.25.1~dev/drivers/driver_greis.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * A Javad GNSS Receiver External Interface Specification (GREIS) driver.
3
 *
4
 * Author(s):
5
 * - Gregory Fong <gregory.fong@virginorbit.com>
6
 *
7
 * Documentation for GREIS can be found at:
8
http://www.javad.com/downloads/javadgnss/manuals/GREIS/GREIS_Reference_Guide.pdf
9
 *
10
 * The version used for reference is that which
11
 * "Reflects Firmware Version 3.6.7, Last revised: August 25, 2016".
12
 *
13
 * This assumes little endian byte order in messages, which is the default, but
14
 * that is configurable. A future improvement could change to read the
15
 * information in [MF] Message Format.
16
 *
17
 * This file is Copyright 2017 Virgin Orbit
18
 * This file is Copyright 2017 the GPSD project
19
 * SPDX-License-Identifier: BSD-2-clause
20
 */
21
22
#include "../include/gpsd_config.h"   // must be before all includes
23
24
#include <assert.h>
25
#include <math.h>
26
#include <stdbool.h>
27
#include <stdio.h>
28
#include <stdlib.h>       // for abs()
29
#include <string.h>
30
#include <sys/select.h>
31
32
#include "../include/bits.h"
33
#include "../include/driver_greis.h"
34
#include "../include/gpsd.h"
35
#include "../include/timespec.h"
36
37
#if defined(GREIS_ENABLE) && defined(BINARY_ENABLE)
38
39
0
#define HEADER_LENGTH 5
40
41
static ssize_t greis_write(struct gps_device_t *session,
42
                           const char *msg, size_t msglen);
43
static const char disable_messages[] = "\%dm\%dm";
44
static const char get_vendor[] = "\%vendor\%print,/par/rcv/vendor";
45
static const char get_ver[] = "\%ver\%print,rcv/ver";
46
static const char set_update_rate_4hz[] = "\%msint\%set,/par/raw/msint,250";
47
48
// Where applicable, the order here is how these will be received per cycle.
49
// TODO: stop hardcoding the cycle time, make it selectable
50
static const char enable_messages_4hz[] =
51
    "\%em\%em,,jps/{RT,UO,GT,PV,SG,DP,SI,EL,AZ,EC,SS,ET}:0.25";
52
53
/*
54
 * GREIS message handlers. The checksum has been already confirmed valid in the
55
 * packet acceptance logic, so we don't need to retest it here.
56
 */
57
58
/**
59
 * Handle the message [RE] Reply
60
 */
61
static gps_mask_t greis_msg_RE(struct gps_device_t *session,
62
                               unsigned char *buf, size_t len)
63
0
{
64
0
    if (0 == memcmp(buf, "%ver%", 5)) {
65
0
        strlcpy(session->subtype, (const char*)&buf[5],
66
0
                sizeof(session->subtype));
67
0
        GPSD_LOG(LOG_DATA, &session->context->errout,
68
0
                 "GREIS: RE, ->subtype: %s\n", session->subtype);
69
0
        return DEVICEID_SET;
70
0
    }
71
72
0
    GPSD_LOG(LOG_INFO, &session->context->errout,
73
0
             "GREIS: RE %3zd, reply: %.*s\n", len, (int)len, buf);
74
0
    return 0;
75
0
}
76
77
/**
78
 * Handle the message [ER] Reply
79
 */
80
static gps_mask_t greis_msg_ER(struct gps_device_t *session,
81
                               unsigned char *buf, size_t len)
82
0
{
83
0
    GPSD_LOG(LOG_WARN, &session->context->errout,
84
0
             "GREIS: ER %3zd, reply: %.*s\n", len, (int)len, buf);
85
0
    return 0;
86
0
}
87
88
/**
89
 * Handle the message [~~](RT) Receiver Time.
90
 */
91
static gps_mask_t greis_msg_RT(struct gps_device_t *session,
92
                               unsigned char *buf, size_t len)
93
0
{
94
0
    if (len < 5) {
95
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
96
0
                 "GREIS: RT bad len %zu\n", len);
97
0
        return 0;
98
0
    }
99
100
0
    session->driver.greis.rt_tod = getleu32(buf, 0);
101
0
    memset(&session->gpsdata.raw, 0, sizeof(session->gpsdata.raw));
102
103
0
    session->driver.greis.seen_rt = true;
104
0
    session->driver.greis.seen_az = false;
105
0
    session->driver.greis.seen_ec = false;
106
0
    session->driver.greis.seen_el = false;
107
0
    session->driver.greis.seen_si = false;
108
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
109
0
             "GREIS: RT, tod: %lu\n",
110
0
             (unsigned long)session->driver.greis.rt_tod);
111
112
0
    return CLEAR_IS;
113
0
}
114
115
/**
116
 * Handle the message [UO] GPS UTC Time Parameters.
117
 */
118
static gps_mask_t greis_msg_UO(struct gps_device_t *session,
119
                               unsigned char *buf, size_t len)
120
0
{
121
    /*
122
     * For additional details on these parameters and the computation done using
123
     * them, refer to the Javad GREIS spec mentioned at the top of this file and
124
     * also to ICD-GPS-200C, Revision IRN-200C-004 April 12, 2000. At the time
125
     * of writing, that could be found at
126
     * https://www.navcen.uscg.gov/pubs/gps/icd200/ICD200Cw1234.pdf .
127
     */
128
0
    uint32_t tot;           // Reference time of week [s]
129
0
    uint16_t wnt;           // Reference week number [dimensionless]
130
0
    int8_t dtls;            // Delta time due to leap seconds [s]
131
0
    uint8_t dn;             // 'Future' reference day number [1..7]
132
0
    uint16_t wnlsf;         // 'Future' reference week number [dimensionless]
133
0
    int8_t dtlsf;           // 'Future' delta time due to leap seconds [s]
134
135
0
    if (len < 24) {
136
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
137
0
                 "GREIS: UO bad len %zu\n", len);
138
0
        return 0;
139
0
    }
140
141
0
    tot = getleu32(buf, 12);
142
0
    wnt = getleu16(buf, 16);
143
0
    dtls = getsb(buf, 18);
144
0
    dn = getub(buf, 19);
145
0
    wnlsf = getleu16(buf, 20);
146
0
    dtlsf = getsb(buf, 22);
147
0
    session->driver.greis.seen_uo = true;
148
149
    /*
150
     * See ICD-GPS-200C 20.3.3.5.2.4 "Universal Coordinated Time (UTC)".
151
     * I totally ripped this off of driver_navcom.c.  Might want to dedupe at
152
     * some point.
153
     */
154
0
    if ((wnt % 256U) * 604800U + tot < wnlsf * 604800U + dn * 86400U) {
155
        // Current time is before effectivity time of the leap second event
156
0
        session->context->leap_seconds = dtls;
157
0
    } else {
158
0
        session->context->leap_seconds = dtlsf;
159
0
    }
160
161
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
162
0
             "GREIS: UO, leap_seconds: %d\n", session->context->leap_seconds);
163
164
0
    return 0;
165
0
}
166
167
/**
168
 * Handle the message [GT] GPS Time.
169
 */
170
static gps_mask_t greis_msg_GT(struct gps_device_t *session,
171
                               unsigned char *buf, size_t len)
172
0
{
173
0
    timespec_t ts_tow;
174
0
    uint32_t tow;            // Time of week [ms]
175
0
    uint16_t wn;             // GPS week number (modulo 1024) [dimensionless]
176
0
    char ts_buf[TIMESPEC_LEN];
177
178
0
    if (len < 7) {
179
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
180
0
                 "GREIS: GT bad len %zu\n", len);
181
0
        return 0;
182
0
    }
183
184
0
    if (!session->driver.greis.seen_uo) {
185
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
186
0
                 "GREIS: can't use GT until after UO has supplied "
187
0
                 "leap second data\n");
188
0
        return 0;
189
0
    }
190
191
0
    tow = getleu32(buf, 0);
192
0
    wn = getleu16(buf, 4);
193
194
0
    MSTOTS(&ts_tow, tow);
195
0
    session->newdata.time = gpsd_gpstime_resolv(session, wn, ts_tow);
196
197
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
198
0
             "GREIS: GT, tow: %" PRIu32 ", wn: %" PRIu16 ", time: %s Leap:%u\n",
199
0
             tow, wn,
200
0
             timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
201
0
             session->context->leap_seconds);
202
203
204
    // save raw.mtime, just in case
205
0
    session->gpsdata.raw.mtime = session->newdata.time;
206
207
0
    return TIME_SET | NTPTIME_IS | ONLINE_SET;
208
0
}
209
210
/**
211
 * Handle the message [PV] Cartesian Position and Velocity.
212
 */
213
static gps_mask_t greis_msg_PV(struct gps_device_t *session,
214
                               unsigned char *buf, size_t len)
215
0
{
216
0
    double x, y, z;         // Cartesian coordinates [m]
217
0
    float p_sigma;          // Position spherical error probability (SEP) [m]
218
0
    float vx, vy, vz;       // Cartesian velocities [m/s]
219
0
    float v_sigma;          // Velocity SEP [m/s]
220
0
    uint8_t solution_type;
221
0
    gps_mask_t mask = 0;
222
223
0
    if (len < 46) {
224
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
225
0
                 "GREIS: PV bad len %zu\n", len);
226
0
        return 0;
227
0
    }
228
229
0
    x = getled64((char *)buf, 0);
230
0
    y = getled64((char *)buf, 8);
231
0
    z = getled64((char *)buf, 16);
232
0
    p_sigma = getlef32((char *)buf, 24);
233
0
    vx = getlef32((char *)buf, 28);
234
0
    vy = getlef32((char *)buf, 32);
235
0
    vz = getlef32((char *)buf, 36);
236
0
    v_sigma = getlef32((char *)buf, 40);
237
0
    solution_type = getub(buf, 44);
238
239
0
    session->newdata.ecef.x = x;
240
0
    session->newdata.ecef.y = y;
241
0
    session->newdata.ecef.z = z;
242
0
    session->newdata.ecef.pAcc = p_sigma;
243
0
    session->newdata.ecef.vx = vx;
244
0
    session->newdata.ecef.vy = vy;
245
0
    session->newdata.ecef.vz = vz;
246
0
    session->newdata.ecef.vAcc = v_sigma;
247
248
    // GREIS Reference Guide 3.4.2 "General Notes" part "Solution Types"
249
0
    if (0 < solution_type &&
250
0
        5 > solution_type) {
251
0
        session->newdata.mode = MODE_3D;
252
0
        if (1 < solution_type) {
253
0
            session->newdata.status = STATUS_DGPS;
254
0
        } else {
255
0
            session->newdata.status = STATUS_GPS;
256
0
        }
257
0
    }
258
259
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
260
0
             "GREIS: PV, ECEF x=%.2f y=%.2f z=%.2f pAcc=%.2f\n",
261
0
             session->newdata.ecef.x,
262
0
             session->newdata.ecef.y,
263
0
             session->newdata.ecef.z,
264
0
             session->newdata.ecef.pAcc);
265
266
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
267
0
             "GREIS: PV, ECEF vx=%.2f vy=%.2f vz=%.2f vAcc=%.2f "
268
0
             "solution_type: %d\n",
269
0
             session->newdata.ecef.vx,
270
0
             session->newdata.ecef.vy,
271
0
             session->newdata.ecef.vz,
272
0
             session->newdata.ecef.vAcc,
273
0
             solution_type);
274
275
0
   mask |= MODE_SET | STATUS_SET | ECEF_SET | VECEF_SET;
276
0
   return mask;
277
0
}
278
279
/**
280
 * Handle the message [SG] Position and Velocity RMS Errors.
281
 */
282
static gps_mask_t greis_msg_SG(struct gps_device_t *session,
283
                               unsigned char *buf, size_t len)
284
0
{
285
0
    float hpos;                 // Horizontal position RMS error [m]
286
0
    float vpos;                 // Vertical position RMS error [m]
287
0
    float hvel;                 // Horizontal velocity RMS error [m/s]
288
0
    float vvel;                 // Vertical velocity RMS error [m/s]
289
290
0
    if (len < 18) {
291
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
292
0
                 "GREIS: SG bad len %zu\n", len);
293
0
        return 0;
294
0
    }
295
296
0
    hpos = getlef32((char *)buf, 0);
297
0
    vpos = getlef32((char *)buf, 4);
298
0
    hvel = getlef32((char *)buf, 8);
299
0
    vvel = getlef32((char *)buf, 12);
300
301
    /*
302
     * All errors are RMS which can be approximated as 1 sigma, so we can just
303
     * use them directly.
304
     *
305
     * Compute missing items in gpsd_error_model(), not here.
306
     */
307
0
    session->newdata.eph = hpos;
308
0
    session->newdata.epv = vpos;
309
0
    session->newdata.eps = hvel;
310
0
    session->newdata.epc = vvel;
311
312
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
313
0
             "GREIS: SG, eph: %.2f, eps: %.2f, epc: %.2f\n",
314
0
             session->newdata.eph,
315
0
             session->newdata.eps, session->newdata.epc);
316
317
0
    return HERR_SET | SPEEDERR_SET | CLIMBERR_SET;
318
0
}
319
320
/**
321
 * Handle the message [DP] Dilution of Precision.
322
 * Note that fill_dop() will handle the unset dops later.
323
 */
324
static gps_mask_t greis_msg_DP(struct gps_device_t *session,
325
                               unsigned char *buf, size_t len)
326
0
{
327
0
    if (len < 18) {
328
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
329
0
                 "GREIS: DP bad len %zu\n", len);
330
0
        return 0;
331
0
    }
332
333
    // clear so that computed DOPs get recomputed.
334
0
    gps_clear_dop(&session->gpsdata.dop);
335
336
0
    session->gpsdata.dop.hdop = getlef32((char *)buf, 0);
337
0
    session->gpsdata.dop.vdop = getlef32((char *)buf, 4);
338
0
    session->gpsdata.dop.tdop = getlef32((char *)buf, 8);
339
340
0
    session->gpsdata.dop.pdop = sqrt(pow(session->gpsdata.dop.hdop, 2) +
341
0
                                     pow(session->gpsdata.dop.vdop, 2));
342
343
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
344
0
             "GREIS: DP, hdop: %.2f, vdop: %.2f, tdop: %.2f, pdop: %.2f\n",
345
0
             session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
346
0
             session->gpsdata.dop.tdop, session->gpsdata.dop.pdop);
347
348
0
    return DOP_SET;
349
0
}
350
351
/**
352
 * Handle the message [SI] Satellite Indices.
353
 *
354
 * This message tells us how many satellites are seen and contains their
355
 * Universal Satellite Identifier (USI).
356
 */
357
static gps_mask_t greis_msg_SI(struct gps_device_t *session,
358
                               unsigned char *buf, size_t len)
359
0
{
360
0
    int i;
361
362
0
    if (len < 1) {
363
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
364
0
                 "GREIS: SI bad len %zu\n", len);
365
0
        return 0;
366
0
    }
367
368
0
    gpsd_zero_satellites(&session->gpsdata);
369
    // FIXME: check against MAXCHANNELS?
370
0
    session->gpsdata.satellites_visible = len - 1;
371
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
372
        // This isn't really PRN, this is USI.  Convert it.
373
0
        unsigned short PRN = getub(buf, i);
374
0
        session->gpsdata.skyview[i].PRN = PRN;
375
376
        // fit into gnssid:svid
377
0
        if (0 == PRN) {
378
            // skip 0 PRN
379
0
            continue;
380
0
        } else if (37 >= PRN) {
381
            // GPS, 1 .. 37
382
0
            session->gpsdata.skyview[i].gnssid = 0;
383
0
            session->gpsdata.skyview[i].svid = PRN;
384
0
        } else if (69 >= PRN) {
385
            // GLONASS, 38 .. 69
386
0
            session->gpsdata.skyview[i].gnssid = 6;
387
0
            session->gpsdata.skyview[i].svid = PRN - 37;
388
0
        } else if (70 == PRN) {
389
            // GLONASS, again, 70
390
0
            session->gpsdata.skyview[i].gnssid = 6;
391
0
            session->gpsdata.skyview[i].svid = 255;
392
0
        } else if (119 >= PRN) {
393
            // Galileo, 71 .. 119
394
0
            session->gpsdata.skyview[i].gnssid = 2;
395
0
            session->gpsdata.skyview[i].svid = PRN - 70;
396
0
        } else if (142 >= PRN) {
397
            // SBAS, 120 .. 142
398
0
            session->gpsdata.skyview[i].gnssid = 1;
399
0
            session->gpsdata.skyview[i].svid = PRN - 119;
400
0
        } else if ((193 <= PRN) &&
401
0
                   (197 >= PRN)) {
402
            // QZSS
403
0
            session->gpsdata.skyview[i].gnssid = 5;
404
0
            session->gpsdata.skyview[i].svid = PRN - 192;
405
0
        } else if ((211 <= PRN) &&
406
0
                   (247 >= PRN)) {
407
            // BeiDou
408
0
            session->gpsdata.skyview[i].gnssid = 3;
409
0
            session->gpsdata.skyview[i].svid = PRN - 210;
410
0
        }
411
0
        session->gpsdata.raw.meas[i].obs_code[0] = '\0';
412
0
        session->gpsdata.raw.meas[i].gnssid =
413
0
            session->gpsdata.skyview[i].gnssid;
414
0
        session->gpsdata.raw.meas[i].svid =
415
0
            session->gpsdata.skyview[i].svid;
416
        // GREIS does not report locktime, so assume max
417
0
        session->gpsdata.raw.meas[i].locktime = LOCKMAX;
418
        // Make sure the unused raw fields are set consistently
419
0
        session->gpsdata.raw.meas[i].sigid = 0;
420
0
        session->gpsdata.raw.meas[i].snr = 0;
421
0
        session->gpsdata.raw.meas[i].freqid = 0;
422
0
        session->gpsdata.raw.meas[i].lli = 0;
423
0
        session->gpsdata.raw.meas[i].codephase = NAN;
424
0
        session->gpsdata.raw.meas[i].deltarange = NAN;
425
0
    }
426
427
0
    session->driver.greis.seen_si = true;
428
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
429
0
             "GREIS: SI, satellites_visible: %d\n",
430
0
             session->gpsdata.satellites_visible);
431
432
0
    return 0;
433
0
}
434
435
/**
436
 * Handle the message [EL] Satellite Elevations.
437
 */
438
static gps_mask_t greis_msg_EL(struct gps_device_t *session,
439
                               unsigned char *buf, size_t len)
440
0
{
441
0
    int i;
442
443
0
    if (!session->driver.greis.seen_si) {
444
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
445
0
                 "GREIS: can't use EL until after SI provides indices\n");
446
0
        return 0;
447
0
    }
448
449
    // check against number of satellites + checksum
450
0
    if (len < session->gpsdata.satellites_visible + 1U) {
451
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
452
0
                 "GREIS: EL bad len %zu, needed at least %d\n", len,
453
0
                 session->gpsdata.satellites_visible + 1);
454
0
        return 0;
455
0
    }
456
457
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
458
0
        short elevation;
459
460
        // GREIS elevation is -90 to 90 degrees
461
        // GREIS uses 127 for n/a
462
        // gpsd uses NAN for n/a, so adjust accordingly
463
0
        elevation = getub(buf, i);
464
0
        if (90 < abs(elevation)) {
465
0
            session->gpsdata.skyview[i].elevation = (double)elevation;
466
0
        }  // else leave as NAN
467
0
    }
468
469
0
    session->driver.greis.seen_el = true;
470
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: EL\n");
471
472
0
    return 0;
473
0
}
474
475
/**
476
 * Handle the message [AZ] Satellite Azimuths.
477
 */
478
static gps_mask_t greis_msg_AZ(struct gps_device_t *session,
479
                               unsigned char *buf, size_t len)
480
0
{
481
0
    int i;
482
483
0
    if (!session->driver.greis.seen_si) {
484
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
485
0
                 "GREIS: can't use AZ until after SI provides indices\n");
486
0
        return 0;
487
0
    }
488
489
    // check against number of satellites + checksum
490
0
    if (len < session->gpsdata.satellites_visible + 1U) {
491
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
492
0
                 "GREIS: AZ bad len %zu, needed at least %d\n", len,
493
0
                 session->gpsdata.satellites_visible + 1);
494
0
        return 0;
495
0
    }
496
497
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
498
0
        short azimuth;
499
500
        /* GREIS azimuth is 0 to 180, multiply by 2 for 0 to 360
501
         * GREIS uses 255 for n/a
502
         * gpsd azimuth is 0 to 359, so adjust accordingly */
503
0
        azimuth = getub(buf, i) * 2;
504
0
        if (360 == azimuth) {
505
0
            session->gpsdata.skyview[i].azimuth = 0;
506
0
        } else if (0 <= azimuth &&
507
0
                   360 > azimuth) {
508
0
            session->gpsdata.skyview[i].azimuth = (double)azimuth;
509
0
        }  // else leave as NAN
510
0
    }
511
512
0
    session->driver.greis.seen_az = true;
513
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: AZ\n");
514
515
0
    return 0;
516
0
}
517
518
/**
519
 * Handle the message [DC] Doppler (CA/L1)
520
 */
521
static gps_mask_t greis_msg_DC(struct gps_device_t *session,
522
                               unsigned char *buf, size_t len)
523
0
{
524
0
    int i;
525
0
    size_t len_needed = (session->gpsdata.satellites_visible * 4) + 1;
526
527
0
    if (!session->driver.greis.seen_si) {
528
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
529
0
                 "GREIS: can't use DC until after SI provides indices\n");
530
0
        return 0;
531
0
    }
532
533
    // check against number of satellites + checksum
534
0
    if (len < len_needed) {
535
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
536
0
                 "GREIS: DC bad len %zu, needed at least %zu\n", len,
537
0
                 len_needed);
538
0
        return 0;
539
0
    }
540
541
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
542
0
        long int_doppler = getles32((char *)buf, i * 4);
543
0
        if (0x7fffffff == int_doppler) {
544
            // out of range
545
0
            session->gpsdata.raw.meas[i].doppler = NAN;
546
0
        } else {
547
0
            session->gpsdata.raw.meas[i].doppler = int_doppler * 1e-4;
548
0
        }
549
0
    }
550
551
0
    session->driver.greis.seen_raw = true;
552
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: DC\n");
553
554
0
    return 0;
555
0
}
556
557
/**
558
 * Handle the message [EC] SNR (CA/L1).
559
 * EC really outputs CNR, but what gpsd refers to as SNR _is_ CNR.
560
 */
561
static gps_mask_t greis_msg_EC(struct gps_device_t *session,
562
                               unsigned char *buf, size_t len)
563
0
{
564
0
    int i;
565
566
0
    if (!session->driver.greis.seen_si) {
567
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
568
0
                 "GREIS: can't use EC until after SI provides indices\n");
569
0
        return 0;
570
0
    }
571
572
    // check against number of satellites + checksum
573
0
    if (len < session->gpsdata.satellites_visible + 1U) {
574
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
575
0
                 "GREIS: EC bad len %zu, needed at least %d\n", len,
576
0
                 session->gpsdata.satellites_visible + 1);
577
0
        return 0;
578
0
    }
579
580
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++)
581
0
        session->gpsdata.skyview[i].ss = getub(buf, i);
582
583
0
    session->driver.greis.seen_ec = true;
584
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: EC\n");
585
586
0
    return 0;
587
0
}
588
589
590
/**
591
 * Handle the message [P3] CA/L2 Carrier Phases, RINEX L2C
592
 */
593
static gps_mask_t greis_msg_P3(struct gps_device_t *session,
594
                               unsigned char *buf, size_t len)
595
0
{
596
0
    int i;
597
0
    size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1;
598
599
0
    if (!session->driver.greis.seen_si) {
600
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
601
0
                 "GREIS: can't use P3 until after SI provides indices\n");
602
0
        return 0;
603
0
    }
604
605
    // check against number of satellites + checksum
606
0
    if (len < len_needed) {
607
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
608
0
                 "GREIS: P3 bad len %zu, needed at least %zu\n", len,
609
0
                 len_needed);
610
0
        return 0;
611
0
    }
612
613
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
614
0
        session->gpsdata.raw.meas[i].l2c = getled64((char *)buf, i * 8);
615
0
    }
616
617
0
    session->driver.greis.seen_raw = true;
618
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: P3\n");
619
620
0
    return 0;
621
0
}
622
623
/**
624
 * Handle the message [PC] CA/L1 Carrier Phases, RINEX L1C
625
 */
626
static gps_mask_t greis_msg_PC(struct gps_device_t *session,
627
                               unsigned char *buf, size_t len)
628
0
{
629
0
    int i;
630
0
    size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1;
631
632
0
    if (!session->driver.greis.seen_si) {
633
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
634
0
                 "GREIS: can't use PC until after SI provides indices\n");
635
0
        return 0;
636
0
    }
637
638
    // check against number of satellites + checksum
639
0
    if (len < len_needed) {
640
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
641
0
                 "GREIS: PC bad len %zu, needed at least %zu\n", len,
642
0
                 len_needed);
643
0
        return 0;
644
0
    }
645
646
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
647
0
        session->gpsdata.raw.meas[i].carrierphase = getled64((char *)buf,
648
0
                                                            i * 8);
649
0
    }
650
651
0
    session->driver.greis.seen_raw = true;
652
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: PC\n");
653
654
0
    return 0;
655
0
}
656
657
/**
658
 * Handle the message [R3] CA/L2 Pseudo-range, RINEX C2C
659
 */
660
static gps_mask_t greis_msg_R3(struct gps_device_t *session,
661
                               unsigned char *buf, size_t len)
662
0
{
663
0
    int i;
664
0
    size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1;
665
666
0
    if (!session->driver.greis.seen_si) {
667
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
668
0
                 "GREIS: can't use R3 until after SI provides indices\n");
669
0
        return 0;
670
0
    }
671
672
    // check against number of satellites + checksum
673
0
    if (len < len_needed) {
674
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
675
0
                 "GREIS: R3 bad len %zu, needed at least %zu\n", len,
676
0
                 len_needed);
677
0
        return 0;
678
0
    }
679
680
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
681
        // get, and convert to meters
682
0
        session->gpsdata.raw.meas[i].c2c = \
683
0
            getled64((char *)buf, i * 8) * CLIGHT;
684
0
    }
685
686
0
    session->driver.greis.seen_raw = true;
687
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: R3\n");
688
689
0
    return 0;
690
0
}
691
692
/**
693
 * Handle the message [RC] Pseudo-range CA/L1, RINEX C1C
694
 */
695
static gps_mask_t greis_msg_RC(struct gps_device_t *session,
696
                               unsigned char *buf, size_t len)
697
0
{
698
0
    int i;
699
0
    size_t len_needed = (session->gpsdata.satellites_visible * 8) + 1;
700
701
0
    if (!session->driver.greis.seen_si) {
702
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
703
0
                 "GREIS: can't use RC until after SI provides indices\n");
704
0
        return 0;
705
0
    }
706
707
    // check against number of satellites + checksum
708
0
    if (len < len_needed) {
709
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
710
0
                 "GREIS: RC bad len %zu, needed at least %zu\n", len,
711
0
                 len_needed);
712
0
        return 0;
713
0
    }
714
715
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
716
        // get, and convert to meters
717
0
        session->gpsdata.raw.meas[i].pseudorange = \
718
0
            getled64((char *)buf, i * 8) * CLIGHT;
719
0
    }
720
721
0
    session->driver.greis.seen_raw = true;
722
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: RC\n");
723
724
0
    return 0;
725
0
}
726
727
/**
728
 * Handle the message [SS] Satellite Navigation Status.
729
 */
730
static gps_mask_t greis_msg_SS(struct gps_device_t *session,
731
                               unsigned char *buf, size_t len)
732
0
{
733
0
    int i;
734
0
    int used_count = 0;
735
736
0
    if (!session->driver.greis.seen_si) {
737
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
738
0
                 "GREIS: can't use SS until after SI provides indices\n");
739
0
        return 0;
740
0
    }
741
742
    // check against number of satellites + solution type + checksum
743
0
    if (len < session->gpsdata.satellites_visible + 2U) {
744
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
745
0
                 "GREIS: SI bad len %zu, needed at least %d\n", len,
746
0
                 session->gpsdata.satellites_visible + 2);
747
0
        return 0;
748
0
    }
749
750
0
    for (i = 0; i < session->gpsdata.satellites_visible; i++) {
751
        /*
752
         * From the GREIS Reference Guide: "Codes [0...3], [40...62], and
753
         * [64...255] indicate that given satellite is used in position
754
         * computation and show which measurements are used. The rest of codes
755
         * indicate that satellite is not used in position computation and
756
         * indicate why this satellite is excluded from position computation."
757
         * Refer to Table 3-4 "Satellite Navigation Status" for the specific
758
         * code meanings.
759
         */
760
0
        uint8_t nav_status = getub(buf, i);
761
0
        session->gpsdata.skyview[i].used =
762
0
            (nav_status <= 3) ||
763
0
            (nav_status >= 40 && nav_status <= 62) ||
764
0
            (nav_status >= 64);
765
766
0
        if (session->gpsdata.skyview[i].used)
767
0
            used_count++;
768
0
    }
769
0
    session->gpsdata.satellites_used = used_count;
770
771
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
772
0
             "GREIS: SS, satellites_used: %d\n",
773
0
             session->gpsdata.satellites_used);
774
775
0
    return used_count ? USED_IS : 0;
776
0
}
777
778
779
/**
780
 * Handle the message [::](ET) Epoch Time.
781
 * This should be kept as the last message in each epoch.
782
 */
783
static gps_mask_t greis_msg_ET(struct gps_device_t *session,
784
                               unsigned char *buf, size_t len)
785
0
{
786
0
    uint32_t tod;
787
0
    gps_mask_t mask = 0;
788
789
0
    if (len < 5) {
790
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
791
0
                 "GREIS: ET bad len %zu\n", len);
792
0
        return 0;
793
0
    }
794
795
0
    if (!session->driver.greis.seen_rt) {
796
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
797
0
                 "GREIS: got ET, but no preceding RT for epoch\n");
798
0
        return 0;
799
0
    }
800
801
0
    tod = getleu32(buf, 0);
802
0
    if (tod != session->driver.greis.rt_tod) {
803
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
804
0
                 "GREIS: broken epoch, RT had %lu, but ET has %lu\n",
805
0
                 (unsigned long)session->driver.greis.rt_tod,
806
0
                 (unsigned long)tod);
807
0
        return 0;
808
0
    }
809
810
    // Skyview time does not differ from time in GT message
811
0
    session->gpsdata.skyview_time.tv_sec = 0;
812
0
    session->gpsdata.skyview_time.tv_nsec = 0;
813
814
0
    GPSD_LOG(LOG_DATA, &session->context->errout,
815
0
             "GREIS: ET, seen: az %d, ec %d, el %d, rt %d, si %d, uo %d\n",
816
0
             (int)session->driver.greis.seen_az,
817
0
             (int)session->driver.greis.seen_ec,
818
0
             (int)session->driver.greis.seen_el,
819
0
             (int)session->driver.greis.seen_rt,
820
0
             (int)session->driver.greis.seen_si,
821
0
             (int)session->driver.greis.seen_uo);
822
823
    // Make sure we got the satellite data, then report it.
824
0
    if ((session->driver.greis.seen_az && session->driver.greis.seen_ec &&
825
0
         session->driver.greis.seen_el && session->driver.greis.seen_si)) {
826
        // Skyview seen, update it.  Go even if no seen_ss or none visible
827
0
        mask |= SATELLITE_SET;
828
829
0
        if (session->driver.greis.seen_raw) {
830
0
            mask |= RAW_IS;
831
0
        } else {
832
0
            session->gpsdata.raw.mtime.tv_sec = 0;
833
0
            session->gpsdata.raw.mtime.tv_nsec = 0;
834
0
        }
835
836
0
    } else {
837
0
        session->gpsdata.raw.mtime.tv_sec = 0;
838
0
        session->gpsdata.raw.mtime.tv_nsec = 0;
839
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
840
0
                 "GREIS: ET: missing satellite details in this epoch\n");
841
0
    }
842
843
0
    GPSD_LOG(LOG_DATA, &session->context->errout, "GREIS: ET, tod: %lu\n",
844
0
             (unsigned long)tod);
845
846
    /* This is a good place to poll firmware version if we need it.
847
     * Waited until now to avoid the startup rush and out of
848
     * critical time path
849
     */
850
0
    if (0 == strnlen(session->subtype, sizeof(session->subtype))) {
851
        // get version
852
0
        (void)greis_write(session, get_ver, sizeof(get_ver) - 1);
853
0
    }
854
    /* The driver waits for ET to send any reports
855
     * Just REPORT_IS is not enough to trigger sending of reports to clients.
856
     * STATUS_SET seems best, if no status by now the status is no fix */
857
0
    return mask | REPORT_IS | STATUS_SET;
858
0
}
859
860
struct dispatch_table_entry {
861
    char id0;
862
    char id1;
863
    gps_mask_t (*handler)(struct gps_device_t *, unsigned char *, size_t);
864
};
865
866
static struct dispatch_table_entry dispatch_table[] = {
867
    {':', ':', greis_msg_ET},
868
    {'A', 'Z', greis_msg_AZ},
869
    {'D', 'C', greis_msg_DC},
870
    {'D', 'P', greis_msg_DP},
871
    {'E', 'C', greis_msg_EC},
872
    {'E', 'R', greis_msg_ER},
873
    {'E', 'L', greis_msg_EL},
874
    {'G', 'T', greis_msg_GT},
875
    {'R', '3', greis_msg_R3},
876
    {'R', 'C', greis_msg_RC},
877
    {'P', '3', greis_msg_P3},
878
    {'P', 'C', greis_msg_PC},
879
    {'P', 'V', greis_msg_PV},
880
    {'R', 'E', greis_msg_RE},
881
    {'S', 'G', greis_msg_SG},
882
    {'S', 'I', greis_msg_SI},
883
    {'S', 'S', greis_msg_SS},
884
    {'U', 'O', greis_msg_UO},
885
    {'~', '~', greis_msg_RT},
886
};
887
888
0
#define dispatch_table_size (sizeof(dispatch_table) / sizeof(dispatch_table[0]))
889
890
/**
891
 * Parse the data from the device
892
 */
893
static gps_mask_t greis_dispatch(struct gps_device_t *session,
894
                                 unsigned char *buf, size_t len)
895
0
{
896
0
    size_t i;
897
0
    char id0, id1;
898
899
0
    if (len == 0)
900
0
        return 0;
901
902
    /*
903
     * This is set because the device reliably signals end of cycle.
904
     * The core library zeroes it just before it calls each driver's
905
     * packet analyzer.
906
     */
907
0
    session->cycle_end_reliable = true;
908
909
    // Length should have already been checked in packet.c, but just in case
910
0
    if (len < HEADER_LENGTH) {
911
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
912
0
                 "GREIS: Packet length %zu shorter than min length\n", len);
913
0
        return 0;
914
0
    }
915
916
    // we may need to dump the raw packet
917
0
    GPSD_LOG(LOG_RAW, &session->context->errout,
918
0
             "GREIS: raw packet id '%c%c'\n", buf[0], buf[1]);
919
920
0
    id0 = buf[0];
921
0
    id1 = buf[1];
922
0
    len -= HEADER_LENGTH;
923
0
    buf += HEADER_LENGTH;
924
925
0
    for (i = 0; i < dispatch_table_size; i++) {
926
0
        struct dispatch_table_entry *entry = &dispatch_table[i];
927
928
0
        if (id0 == entry->id0 && id1 == entry->id1) {
929
0
            return entry->handler(session, buf, len);
930
0
        }
931
0
    }
932
933
0
    GPSD_LOG(LOG_WARN, &session->context->errout,
934
0
             "GREIS: unknown packet id '%c%c' length %zu\n", id0, id1, len);
935
0
    return 0;
936
0
}
937
938
/**********************************************************
939
 *
940
 * Externally called routines below here
941
 *
942
 **********************************************************/
943
944
/**
945
 * Write data to the device with checksum.
946
 * Returns number of bytes written on successful write, -1 otherwise.
947
 */
948
static ssize_t greis_write(struct gps_device_t *session,
949
                           const char *msg, size_t msglen)
950
0
{
951
0
    char checksum_str[3] = {0};
952
0
    ssize_t count;
953
954
0
    if (session->context->readonly) {
955
        // readonly mode, do not write anything
956
0
        return -1;
957
0
    }
958
959
0
    if (NULL == msg) {
960
        /* We do sometimes write zero length to wake up GPS,
961
         * so just test for NULL msg, not zero length message */
962
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
963
0
                 "GREIS: nothing to write\n");
964
0
        return -1;
965
0
    }
966
967
    // Account for length + checksum marker + checksum + \r + \n + \0
968
0
    if (msglen + 6 > sizeof(session->msgbuf)) {
969
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
970
0
                 "GREIS: msgbuf is smaller than write length %zu\n", msglen);
971
0
        return -1;
972
0
    }
973
974
0
    memcpy(&session->msgbuf[0], msg, msglen);
975
976
0
    if (msglen == 0) {
977
        // This is a dummy write, don't give a checksum.
978
0
        session->msgbuf[0] = '\n';
979
0
        session->msgbuflen = 1;
980
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
981
0
                 "GREIS: Dummy write\n");
982
0
    } else {
983
0
        unsigned char checksum;
984
985
0
        session->msgbuflen = msglen;
986
0
        session->msgbuf[session->msgbuflen++] = '@';  // checksum marker
987
988
        // calculate checksum with @, place at end, and set length to write
989
0
        checksum = greis_checksum((unsigned char *)session->msgbuf,
990
0
                                  session->msgbuflen);
991
0
        (void)snprintf(checksum_str, sizeof(checksum_str), "%02X", checksum);
992
0
        session->msgbuf[session->msgbuflen++] = checksum_str[0];
993
0
        session->msgbuf[session->msgbuflen++] = checksum_str[1];
994
0
        session->msgbuf[session->msgbuflen++] = '\r';
995
0
        session->msgbuf[session->msgbuflen++] = '\n';
996
997
0
        GPSD_LOG(LOG_PROG, &session->context->errout,
998
0
                 "GREIS: Writing command '%.*s', checksum: %s\n",
999
0
                 (int)msglen, msg, checksum_str);
1000
0
    }
1001
0
    session->msgbuf[session->msgbuflen] = '\0';
1002
0
    count = gpsd_write(session, session->msgbuf, session->msgbuflen);
1003
1004
0
    if (count != (ssize_t)session->msgbuflen) {
1005
0
        return -1;
1006
0
    }  // else
1007
1008
0
    return count;
1009
0
}
1010
1011
/**
1012
 * Write data to the device, doing any required padding or checksumming
1013
 */
1014
static ssize_t greis_control_send(struct gps_device_t *session,
1015
                                  char *msg, size_t msglen)
1016
0
{
1017
0
    return greis_write(session, msg, msglen);
1018
0
}
1019
1020
static void greis_event_hook(struct gps_device_t *session, event_t event)
1021
0
{
1022
0
    if (session->context->readonly ||
1023
0
        session->context->passive) {
1024
0
        return;
1025
0
    }
1026
1027
0
    if (event == event_wakeup) {
1028
        /*
1029
         * Code to make the device ready to communicate.  Only needed if the
1030
         * device is in some kind of sleeping state, and only shipped to
1031
         * RS232C, so that gpsd won't send strings to unidentified USB devices
1032
         * that might not be GPSes at all.
1033
         */
1034
1035
        /*
1036
         * Disable any existing messages, then request vendor for
1037
         * identification.
1038
         */
1039
0
        (void)greis_write(session, disable_messages,
1040
0
                          sizeof(disable_messages) - 1);
1041
0
        (void)greis_write(session, get_vendor, sizeof(get_vendor) - 1);
1042
0
    } else if (event == event_identified ||
1043
0
               event == event_reactivate) {
1044
        /*
1045
         * Fires when the first full packet is recognized from a previously
1046
         * unidentified device OR the device is reactivated after close. The
1047
         * session.lexer counter is zeroed.
1048
         *
1049
         * TODO: If possible, get the software version and store it in
1050
         * session->subtype.
1051
         */
1052
0
        (void)greis_write(session, disable_messages,
1053
0
                          sizeof(disable_messages) - 1);
1054
0
        (void)greis_write(session, set_update_rate_4hz,
1055
0
                          sizeof(set_update_rate_4hz) - 1);
1056
0
        (void)greis_write(session, enable_messages_4hz,
1057
0
                          sizeof(enable_messages_4hz) - 1);
1058
1059
        // Store (expected) cycle time (seconds)
1060
0
        session->gpsdata.dev.cycle.tv_sec = 0;
1061
0
        session->gpsdata.dev.cycle.tv_nsec = 250000000L;
1062
0
    } else if (event == event_driver_switch) {
1063
        /*
1064
         * Fires when the driver on a device is changed *after* it
1065
         * has been identified.
1066
         */
1067
0
    } else if (event == event_deactivate) {
1068
        /*
1069
         * Fires when the device is deactivated.  Use this to revert
1070
         * whatever was done at event_identified and event_configure
1071
         * time.
1072
         */
1073
0
        (void)greis_write(session, disable_messages,
1074
0
                          sizeof(disable_messages) - 1);
1075
0
    }
1076
0
}
1077
1078
/**
1079
 * This is the entry point to the driver. When the packet sniffer recognizes
1080
 * a packet for this driver, it calls this method which passes the packet to
1081
 * the binary processor or the nmea processor, depending on the session type.
1082
 */
1083
static gps_mask_t greis_parse_input(struct gps_device_t *session)
1084
0
{
1085
0
    if (GREIS_PACKET == session->lexer.type) {
1086
0
        return greis_dispatch(session, session->lexer.outbuffer,
1087
0
                              session->lexer.outbuflen);
1088
0
    }
1089
0
    if (NMEA_PACKET == session->lexer.type) {
1090
0
        return nmea_parse((char *)session->lexer.outbuffer, session);
1091
0
    }
1092
0
    return 0;
1093
0
}
1094
1095
/**
1096
 * Set port operating mode, speed, parity, stopbits etc. here.
1097
 * Note: parity is passed as 'N'/'E'/'O', but you should program
1098
 * defensively and allow 0/1/2 as well.
1099
 */
1100
static bool greis_set_speed(struct gps_device_t *session,
1101
                            speed_t speed, char parity, int stopbits)
1102
0
{
1103
    // change on current port
1104
0
    static const char set_rate[] = "set,/par/cur/term/rate,";
1105
0
    static const char set_parity[] = "set,/par/cur/term/parity,";
1106
0
    static const char set_stops[] = "set,/par/cur/term/stops,";
1107
0
    static const char parity_none[] = "N";
1108
0
    static const char parity_even[] = "even";
1109
0
    static const char parity_odd[] = "odd";
1110
1111
0
    char command[BUFSIZ] = {0};
1112
0
    const char *selected_parity = NULL;
1113
1114
0
    switch (parity) {
1115
0
    case 'N':
1116
0
    case 0:
1117
0
        selected_parity = parity_none;
1118
0
        break;
1119
0
    case 'E':
1120
0
    case 1:
1121
0
        selected_parity = parity_even;
1122
0
        break;
1123
0
    case 'O':
1124
0
    case 2:
1125
0
        selected_parity = parity_odd;
1126
0
        break;
1127
0
    default:
1128
0
        return false;
1129
0
    }
1130
1131
0
    (void)snprintf(command, sizeof(command) - 1, "%s%lu && %s%s && %s%d",
1132
0
             set_rate, (unsigned long)speed, set_parity, selected_parity,
1133
0
             set_stops, stopbits);
1134
0
    return (bool)greis_write(session, command,
1135
0
                             strnlen(command, sizeof(command)));
1136
0
}
1137
1138
#if 0
1139
/**
1140
 * TODO: Switch between NMEA and binary mode
1141
 */
1142
static void greis_set_mode(struct gps_device_t *session, int mode)
1143
{
1144
    if (mode == MODE_NMEA) {
1145
        // send a mode switch control string
1146
    } else {
1147
        // send a mode switch control string
1148
    }
1149
}
1150
#endif
1151
1152
#if 0  // TODO
1153
static double greis_time_offset(struct gps_device_t *session)
1154
{
1155
    /*
1156
     * If NTP notification is enabled, the GPS will occasionally NTP
1157
     * its notion of the time. This will lag behind actual time by
1158
     * some amount which has to be determined by observation vs. (say
1159
     * WWVB radio broadcasts) and, furthermore, may differ by baud
1160
     * rate. This method is for computing the NTP fudge factor.  If
1161
     * it's absent, an offset of 0.0 will be assumed, effectively
1162
     * falling back on what's in ntp.conf. When it returns NAN,
1163
     * nothing will be sent to NTP.
1164
     */
1165
    return MAGIC_CONSTANT;
1166
}
1167
#endif
1168
1169
// This is everything we export
1170
// *INDENT-OFF*
1171
const struct gps_type_t driver_greis = {
1172
    // Full name of type
1173
    .type_name        = "GREIS",
1174
    // Associated lexer packet type
1175
    .packet_type      = GREIS_PACKET,
1176
    // Driver type flags
1177
    .flags            = DRIVER_STICKY,
1178
    // Response string that identifies device (not active)
1179
    .trigger          = NULL,
1180
    // Number of satellite channels supported by the device
1181
    .channels         = 128,
1182
    // Startup-time device detector
1183
    .probe_detect     = NULL,
1184
    // Packet getter (using default routine)
1185
    .get_packet       = packet_get1,
1186
    // Parse message packets
1187
    .parse_packet     = greis_parse_input,
1188
    // non-perturbing initial query (e.g. for version)
1189
    .init_query        = NULL,
1190
    // fire on various lifetime events
1191
    .event_hook       = greis_event_hook,
1192
    // Speed (baudrate) switch
1193
    .speed_switcher   = greis_set_speed,
1194
#if 0  // TODO
1195
    // Switch to NMEA mode
1196
    .mode_switcher    = greis_set_mode,
1197
#endif
1198
    // Message delivery rate switcher (not active)
1199
    .rate_switcher    = NULL,
1200
    /* Minimum cycle time of the device.
1201
     * Default is 1/100, but this is tunable using /par/raw/msint . */
1202
    .min_cycle.tv_sec  = 0,
1203
    .min_cycle.tv_nsec = 10000000,
1204
    // Control string sender - should provide checksum and headers/trailer
1205
    .control_send   = greis_control_send,
1206
    .time_offset     = NULL,
1207
// *INDENT-ON*
1208
};
1209
#endif  // defined(GREIS_ENABLE) && defined(BINARY_ENABLE)
1210
// vim: set expandtab shiftwidth=4