Coverage Report

Created: 2026-10-12 06:10

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