Coverage Report

Created: 2026-06-04 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpsd/gpsd-3.27.6~dev/drivers/driver_geostar.c
Line
Count
Source
1
/*
2
 * This is the gpsd driver for GeoStar Navigation receivers
3
 * operating in binary mode.
4
 *
5
 * Tested with GeoS-1M GPS/GLONASS receiver.
6
 *
7
 * By Viktar Palstsiuk, viktar.palstsiuk@promwad.com
8
 *
9
 * This file is Copyright by the GPSD project
10
 * SPDX-License-Identifier: BSD-2-clause
11
 */
12
13
#include "../include/gpsd_config.h"   // must be before all includes
14
15
#include <math.h>
16
#include <stdbool.h>
17
#include <stdio.h>
18
#include <string.h>
19
20
#include "../include/bits.h"
21
#include "../include/gpsd.h"
22
#include "../include/strfuncs.h"
23
#include "../include/timespec.h"
24
25
#ifdef GEOSTAR_ENABLE
26
678
#define GEOSTAR_CHANNELS   24
27
28
85
#define JAN_2008           0x47798280  // 1199145600 = 2008 - 1970 in seconds
29
30
1.80k
#define OFFSET(n)          ((n)*4+4)
31
32
8.13k
static int decode_channel_id (uint32_t ch_id) {
33
8.13k
        int num = 0;
34
8.13k
        num = (int)(ch_id & 0x1F);              // SV ID
35
8.13k
        if (0 == (ch_id & (1<<30))) {
36
7.86k
            num += GLONASS_PRN_OFFSET;          // GLONASS SV
37
7.86k
        } else if (0 == num) {
38
0
            num = 32;                           // GPS SV
39
0
        }
40
8.13k
        return num;
41
8.13k
}
42
43
/* write to geostar
44
 *
45
 * id - message is
46
 * data - 32 bit words of message content
47
 * len - number of 32 bit words in data
48
 *
49
 * return: 0 == OK
50
 *         negative on error
51
 */
52
static int geostar_write(struct gps_device_t *session,
53
                         unsigned int id, const unsigned char *data,
54
                         size_t len)
55
662
{
56
662
    int i;
57
662
    unsigned long cs = 0;
58
662
    char buf2[64];
59
60
662
    if (sizeof(session->msgbuf) < ((len * 4) + 12)) {
61
62
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
63
0
                 "geostar_write() write too long  %zu, %s\n", len,
64
0
                 gps_hexdump(buf2, sizeof(buf2), data, len * 4));
65
0
        return -1;
66
0
    }
67
662
    putbyte(session->msgbuf, 0, 'P');
68
662
    putbyte(session->msgbuf, 1, 'S');
69
662
    putbyte(session->msgbuf, 2, 'G');
70
662
    putbyte(session->msgbuf, 3, 'G');
71
72
662
    putbe16(session->msgbuf, 4, id);
73
662
    putbe16(session->msgbuf, 6, len);
74
75
    // Copy content
76
662
    memcpy(session->msgbuf + 8, data, len * 4);
77
78
662
    len += 2;      // PSGG + id + len
79
80
    // Calculate checksum
81
2.69k
    for (i = 0; (size_t)i < len; i++) {
82
2.03k
        cs ^= getleu32(session->msgbuf, i * 4);
83
2.03k
    }
84
85
662
    putle32(session->msgbuf, len * 4, cs);
86
87
662
    len += 1;    // Checksum
88
89
662
    session->msgbuflen = len * 4;
90
91
662
    GPSD_LOG(LOG_PROG, &session->context->errout,
92
662
             "Sent GeoStar packet id 0x%x (%s)\n", id,
93
662
             gps_hexdump(buf2, sizeof(buf2),
94
662
                         (const unsigned char*)session->msgbuf,
95
662
                         session->msgbuflen));
96
662
    if (gpsd_write(session, session->msgbuf, session->msgbuflen) !=
97
662
        (ssize_t)session->msgbuflen) {
98
662
        return -1;
99
662
    }
100
101
0
    return 0;
102
662
}
103
104
/* geostar_detect()
105
 *
106
 * see if it looks like a GeoStar device is listening and
107
 * return 1 if found, 0 if not
108
 */
109
static bool geostar_detect(struct gps_device_t *session)
110
0
{
111
0
    unsigned char buf[1 * 4];
112
0
    bool ret = false;
113
0
    int myfd;
114
115
0
    myfd = session->gpsdata.gps_fd;
116
117
    // request firmware revision and look for a valid response
118
0
    putbe32(buf, 0, 0);
119
0
    if (0 == geostar_write(session, 0xc1, buf, 1)) {
120
0
        unsigned int n;
121
0
        struct timespec to;
122
123
        // FIXME: this holds the main loop from running...
124
0
        for (n = 0; n < 3; n++) {
125
            // wait one second
126
0
            to.tv_sec = 1;
127
0
            to.tv_nsec = 0;
128
0
            if (!nanowait(myfd, &to)) {
129
0
                break;
130
0
            }
131
0
            if (0 <= packet_get1(session)) {
132
0
                if (session->lexer.type == GEOSTAR_PACKET) {
133
0
                    GPSD_LOG(LOG_RAW, &session->context->errout,
134
0
                             "geostar_detect found\n");
135
0
                    ret = true;
136
0
                    break;
137
0
                }
138
0
            }
139
0
        }
140
0
    }
141
142
0
    return ret;
143
0
}
144
145
static gps_mask_t geostar_analyze(struct gps_device_t *session)
146
4.40k
{
147
4.40k
    int i;
148
4.40k
    gps_mask_t mask = 0;
149
4.40k
    unsigned int id;
150
4.40k
    uint16_t uw1, uw2;
151
4.40k
    uint32_t ul1, ul2, ul3, ul4, ul5;
152
4.40k
    double d1, d2, d3, d4, d5;
153
4.40k
    char buf[sizeof(session->lexer.outbuffer)];
154
4.40k
    char buf2[sizeof(session->lexer.outbuffer) * 3];
155
156
4.40k
    if (GEOSTAR_PACKET != session->lexer.type) {
157
0
        GPSD_LOG(LOG_INF, &session->context->errout,
158
0
                 "geostar_analyze packet type %d\n",
159
0
                 session->lexer.type);
160
0
        return 0;
161
0
    }
162
163
4.40k
    if (12 > session->lexer.outbuflen ||
164
4.40k
        'P' != session->lexer.outbuffer[0]) {
165
71
        GPSD_LOG(LOG_WARN, &session->context->errout,
166
71
                 "geostar_analyze invalid packet\n");
167
71
        return 0;
168
71
    }
169
4.33k
    if (sizeof(buf) <= session->lexer.outbuflen) {
170
0
        GPSD_LOG(LOG_WARN, &session->context->errout,
171
0
                 "geostar_analyze overlong packet %zd\n",
172
0
                 session->lexer.outbuflen);
173
0
        return 0;
174
0
    }
175
176
    // put data part of message in buf
177
178
4.33k
    memset(buf, 0, sizeof(buf));
179
4.33k
    memcpy(buf, session->lexer.outbuffer, session->lexer.outbuflen);
180
181
4.33k
    id = (unsigned int)getleu16(session->lexer.outbuffer, OFFSET(0));
182
183
4.33k
    GPSD_LOG(LOG_DATA, &session->context->errout,
184
4.33k
             "GeoStar packet id 0x%02x length %zd: %s\n",
185
4.33k
             id, session->lexer.outbuflen,
186
4.33k
             gps_hexdump(buf2, sizeof(buf2), (unsigned char*)buf,
187
4.33k
                         session->lexer.outbuflen));
188
189
4.33k
    session->cycle_end_reliable = true;
190
191
4.33k
    switch (id) {
192
79
    case 0x10:
193
79
        GPSD_LOG(LOG_INF, &session->context->errout, "Raw measurements\n");
194
79
        break;
195
78
    case 0x11:
196
78
        GPSD_LOG(LOG_INF, &session->context->errout, "GPS sub-frame data\n");
197
78
        break;
198
73
    case 0x12:
199
73
        GPSD_LOG(LOG_INF, &session->context->errout,
200
73
                 "GLONASS sub-frame data\n");
201
73
        break;
202
113
    case 0x13:
203
113
        d1 = getled64(buf, OFFSET(1));
204
113
        d2 = getled64(buf, OFFSET(3));
205
113
        d3 = getled64(buf, OFFSET(5));
206
113
        d4 = getled64(buf, OFFSET(29));    // GPS time
207
113
        d5 = getled64(buf, OFFSET(31));    // GLONASS time
208
113
        GPSD_LOG(LOG_INF, &session->context->errout,
209
113
                 "ECEF coordinates %g %g %g %f %f\n", d1, d2, d3, d4, d5);
210
113
        break;
211
85
    case 0x20:
212
85
        d1 = getled64(buf, OFFSET(1)); /* time */
213
214
85
        DTOTS(&session->newdata.time, d1);
215
85
        session->newdata.time.tv_sec += JAN_2008;
216
217
85
        session->newdata.latitude = getled64(buf, OFFSET(3)) * RAD_2_DEG;
218
85
        session->newdata.longitude = getled64(buf, OFFSET(5)) * RAD_2_DEG;
219
        // altitude above ellipsoid
220
85
        session->newdata.altHAE = getled64(buf, OFFSET(7));
221
85
        session->newdata.geoid_sep = getled64(buf, OFFSET(9));
222
85
        session->gpsdata.satellites_used = (int)getles32(buf, OFFSET(11));
223
85
        session->gpsdata.dop.gdop = getled64(buf, OFFSET(13));
224
85
        session->gpsdata.dop.pdop = getled64(buf, OFFSET(15));
225
85
        session->gpsdata.dop.tdop = getled64(buf, OFFSET(17));
226
85
        session->gpsdata.dop.hdop = getled64(buf, OFFSET(19));
227
85
        session->gpsdata.dop.vdop = getled64(buf, OFFSET(21));
228
85
        session->newdata.speed = getled64(buf, OFFSET(31));
229
85
        session->newdata.track = getled64(buf, OFFSET(33)) * RAD_2_DEG;
230
231
85
        ul1 = getleu32(buf, OFFSET(29));   // status
232
233
85
        if (0 != ul1) {
234
0
            session->newdata.status = STATUS_UNK;
235
0
            mask |= STATUS_SET;
236
85
        } else if (STATUS_GPS > session->newdata.status) {
237
            // Don't step on previously set status
238
85
            session->newdata.status = STATUS_GPS;
239
85
            mask |= STATUS_SET;
240
85
        }
241
85
        mask |= TIME_SET | NTPTIME_IS | LATLON_SET | ALTITUDE_SET |
242
85
                SPEED_SET | STATUS_SET | TRACK_SET | DOP_SET | USED_IS |
243
85
                REPORT_IS;
244
245
85
        GPSD_LOG(LOG_INF, &session->context->errout,
246
85
                 "Geographic coordinates %f %g %g %g %g %g\n",
247
85
                 d1,
248
85
                 session->newdata.latitude,
249
85
                 session->newdata.longitude,
250
85
                 session->newdata.altHAE,
251
85
                 session->newdata.speed,
252
85
                 session->newdata.track);
253
85
        GPSD_LOG(LOG_INF, &session->context->errout,
254
85
                 "Dilution of precision %g %g %g %g %g\n",
255
85
                 session->gpsdata.dop.gdop,
256
85
                 session->gpsdata.dop.pdop,
257
85
                 session->gpsdata.dop.tdop,
258
85
                 session->gpsdata.dop.hdop,
259
85
                 session->gpsdata.dop.vdop);
260
85
        break;
261
137
    case 0x21:
262
137
        ul1 = getleu32(buf, OFFSET(1));
263
137
        ul2 = getleu32(buf, OFFSET(2));
264
137
        uw1 = getleu16(buf, OFFSET(3));
265
137
        uw2 = getleu16(buf, OFFSET(3) + 2);
266
137
        GPSD_LOG(LOG_INF, &session->context->errout,
267
137
                 "Current receiver telemetry %x %d %d %d\n",
268
137
                 ul1, ul2, uw1, uw2);
269
137
        if (ul1 & (1<<3)) {
270
70
            session->newdata.mode = MODE_2D;
271
70
        } else {
272
67
            session->newdata.mode = MODE_3D;
273
67
        }
274
137
        if (ul1 & (1<<2)) {
275
70
            session->newdata.status = STATUS_GPS;
276
70
        } else {
277
67
            session->newdata.status = STATUS_UNK;
278
67
            session->newdata.mode = MODE_NO_FIX;
279
67
        }
280
281
137
        mask |= MODE_SET | STATUS_SET;
282
137
        break;
283
339
    case 0x22:
284
339
        ul1 = getleu32(buf, OFFSET(1));
285
339
        if (GEOSTAR_CHANNELS < ul1) {
286
339
            ul1 = GEOSTAR_CHANNELS;
287
339
        }
288
339
        GPSD_LOG(LOG_INF, &session->context->errout, "SVs in view %d\n", ul1);
289
339
        session->gpsdata.satellites_visible = (int)ul1;
290
8.47k
        for(i = 0; (uint32_t)i < ul1; i++) {
291
8.13k
            int16_t s1, s2, s3;
292
8.13k
            ul2 = getleu32(buf, OFFSET(2) + i * 3 * 4);
293
8.13k
            s1 = getles16(buf, OFFSET(3) + i * 3 * 4);
294
8.13k
            s2 = getles16(buf, OFFSET(3) + 2 + i * 3 * 4);
295
8.13k
            s3 = getles16(buf, OFFSET(4) + 2 + i * 3 * 4);
296
8.13k
            GPSD_LOG(LOG_INF, &session->context->errout,
297
8.13k
                     "ID %d Az %g El %g SNR %g\n",
298
8.13k
                     decode_channel_id(ul2), s1 * 0.001 * RAD_2_DEG,
299
8.13k
                     s2 * 0.001 * RAD_2_DEG, s3 * 0.1);
300
8.13k
            session->gpsdata.skyview[i].PRN = (short)decode_channel_id(ul2);
301
8.13k
            session->gpsdata.skyview[i].azimuth =
302
8.13k
                    (short)round((double)s1 * 0.001 * RAD_2_DEG);
303
8.13k
            session->gpsdata.skyview[i].elevation =
304
8.13k
                    (short)round((double)s2 * 0.001 * RAD_2_DEG);
305
8.13k
            session->gpsdata.skyview[i].ss = (double)s3*0.1;
306
8.13k
            session->gpsdata.skyview[i].used = (bool)(ul2 & (1<<27));
307
8.13k
        }
308
339
        session->gpsdata.skyview_time.tv_sec = 0;
309
339
        session->gpsdata.skyview_time.tv_nsec = 0;
310
339
        mask |= SATELLITE_SET | USED_IS;
311
339
        break;
312
112
    case 0x3e:
313
112
        ul1 = getleu32(buf, OFFSET(1));
314
112
        ul2 = getleu32(buf, OFFSET(2));
315
112
        ul3 = getleu32(buf, OFFSET(3));
316
112
        GPSD_LOG(LOG_INF, &session->context->errout,
317
112
                 "Receiver power-up message %d %d %d\n", ul1, ul2, ul3);
318
112
        break;
319
74
    case 0x3f:
320
74
        ul1 = getleu32(buf, OFFSET(1));
321
74
        ul2 = getleu32(buf, OFFSET(2));
322
74
        GPSD_LOG(LOG_WARN, &session->context->errout,
323
74
                 "Negative acknowledge %x %d\n", ul1, ul2);
324
74
        break;
325
64
    case 0x40:
326
64
        GPSD_LOG(LOG_INF, &session->context->errout,
327
64
                 "Response to Set initial parameters\n");
328
64
        break;
329
71
    case 0x41:
330
71
        GPSD_LOG(LOG_INF, &session->context->errout,
331
71
                 "Response to Set serial ports parameters\n");
332
71
        break;
333
124
    case 0x42:
334
124
        ul1 = getleu32(buf, OFFSET(1));
335
124
        ul2 = getleu32(buf, OFFSET(2));
336
124
        ul3 = getleu32(buf, OFFSET(3));
337
124
        GPSD_LOG(LOG_INF, &session->context->errout,
338
124
                 "Response to Set receiver operation mode %d %d %d\n",
339
124
                 ul1, ul2, ul3);
340
124
        break;
341
64
    case 0x43:
342
64
        GPSD_LOG(LOG_INF, &session->context->errout,
343
64
                 "Response to Set navigation task solution parameters\n");
344
64
        break;
345
56
    case 0x44:
346
56
        GPSD_LOG(LOG_INF, &session->context->errout,
347
56
                 "Response to Set output data rate\n");
348
56
        break;
349
66
    case 0x46:
350
66
        GPSD_LOG(LOG_INF, &session->context->errout,
351
66
                 "Response to Assign data protocol to communication port\n");
352
66
        break;
353
67
    case 0x48:
354
67
        GPSD_LOG(LOG_INF, &session->context->errout,
355
67
                 "Response to Set GPS almanac\n");
356
67
        break;
357
79
    case 0x49:
358
79
        GPSD_LOG(LOG_INF, &session->context->errout,
359
79
                 "Response to Set GLONASS almanac\n");
360
79
        break;
361
77
    case 0x4a:
362
77
        GPSD_LOG(LOG_INF, &session->context->errout,
363
77
                 "Response to Set GPS ephemeris\n");
364
77
        break;
365
64
    case 0x4b:
366
64
        GPSD_LOG(LOG_INF, &session->context->errout,
367
64
                 "Response to Set GLONASS ephemeris\n");
368
64
        break;
369
69
    case 0x4c:
370
69
        ul1 = getleu32(buf, OFFSET(1));
371
69
        ul2 = getleu32(buf, OFFSET(2));
372
69
        ul3 = getleu32(buf, OFFSET(3));
373
69
        ul4 = getleu32(buf, OFFSET(4));
374
69
        ul5 = getleu32(buf, OFFSET(5));
375
69
        GPSD_LOG(LOG_INF, &session->context->errout,
376
69
                 "Response to Set PPS parameters %d %d %d %d %d\n",
377
69
                 ul1, ul2, ul3, ul4, ul5);
378
69
        break;
379
72
    case 0x4d:
380
72
        GPSD_LOG(LOG_INF, &session->context->errout,
381
72
                 "Response to Enable/disable SV in position fix\n");
382
72
        break;
383
71
    case 0x4e:
384
71
        GPSD_LOG(LOG_INF, &session->context->errout,
385
71
                 "Response to Enable/disable NMEA messages\n");
386
71
        break;
387
59
    case 0x4f:
388
59
        ul1 = getleu32(buf, OFFSET(1));
389
59
        ul2 = getleu32(buf, OFFSET(2));
390
59
        GPSD_LOG(LOG_INF, &session->context->errout,
391
59
                 "Response to Enable/disable binary messages %x %x\n",
392
59
                 ul1, ul2);
393
59
        break;
394
75
    case 0x80:
395
75
        GPSD_LOG(LOG_INF, &session->context->errout,
396
75
                 "Response to Query initial parameters\n");
397
75
        break;
398
112
    case 0x81:
399
112
        GPSD_LOG(LOG_INF, &session->context->errout,
400
112
                 "Response to Query serial ports parameters\n");
401
112
        break;
402
84
    case 0x82:
403
84
        ul1 = getleu32(buf, OFFSET(1));
404
84
        ul2 = getleu32(buf, OFFSET(2));
405
84
        ul3 = getleu32(buf, OFFSET(3));
406
84
        GPSD_LOG(LOG_INF, &session->context->errout,
407
84
                 "Response to Query receiver operation mode %d %d %d\n",
408
84
                 ul1, ul2, ul3);
409
84
        break;
410
116
    case 0x83:
411
116
        GPSD_LOG(LOG_INF, &session->context->errout,
412
116
                 "Response to Query navigation task solution parameters\n");
413
116
        break;
414
77
    case 0x84:
415
77
        GPSD_LOG(LOG_INF, &session->context->errout,
416
77
                 "Response to Query output data rate\n");
417
77
        break;
418
73
    case 0x86:
419
73
        session->driver.geostar.physical_port =
420
73
            (unsigned int)getleu32(buf, OFFSET(1));
421
422
73
        GPSD_LOG(LOG_INF, &session->context->errout,
423
73
                 "Response to Query data protocol assignment to "
424
73
                 "communication port\n");
425
73
        GPSD_LOG(LOG_INF, &session->context->errout,
426
73
                 "Connected to physical port %d\n",
427
73
                 session->driver.geostar.physical_port);
428
73
        break;
429
70
    case 0x88:
430
70
        GPSD_LOG(LOG_INF, &session->context->errout,
431
70
                 "Response to Query GPS almanac\n");
432
70
        break;
433
457
    case 0x89:
434
457
        GPSD_LOG(LOG_INF, &session->context->errout,
435
457
                 "Response to Query GLONASS almanac\n");
436
457
        break;
437
79
    case 0x8a:
438
79
        GPSD_LOG(LOG_INF, &session->context->errout,
439
79
                 "Response to Query GPS ephemerides\n");
440
79
        break;
441
74
    case 0x8b:
442
74
        d1 = getled64(buf, OFFSET(23));
443
74
        d2 = getled64(buf, OFFSET(25));
444
74
        d3 = getled64(buf, OFFSET(27));
445
74
        GPSD_LOG(LOG_INF, &session->context->errout,
446
74
                 "Response to Query GLONASS ephemerides %g %g %g\n",
447
74
                 d1, d2, d3);
448
74
        break;
449
100
    case 0x8c:
450
100
        ul1 = getleu32(buf, OFFSET(1));
451
100
        ul2 = getleu32(buf, OFFSET(2));
452
100
        ul3 = getleu32(buf, OFFSET(3));
453
100
        ul4 = getleu32(buf, OFFSET(4));
454
100
        ul5 = getleu32(buf, OFFSET(5));
455
100
        GPSD_LOG(LOG_INF, &session->context->errout,
456
100
                 "Response to Query PPS parameters %d %d %d %d %d\n",
457
100
                 ul1, ul2, ul3, ul4, ul5);
458
100
        break;
459
56
    case 0x8d:
460
56
        GPSD_LOG(LOG_INF, &session->context->errout,
461
56
                 "Response to Query enable/disable status of "
462
56
                 "the SV in position fix\n");
463
56
        break;
464
74
    case 0x8e:
465
74
        GPSD_LOG(LOG_INF, &session->context->errout,
466
74
                 "Response to Query enable NMEA messages\n");
467
74
        break;
468
66
    case 0x8f:
469
66
        ul1 = getleu32(buf, OFFSET(1));
470
66
        ul2 = getleu32(buf, OFFSET(2));
471
66
        GPSD_LOG(LOG_INF, &session->context->errout,
472
66
                 "Response to Query enable binary messages %x %x\n",
473
66
                 ul1, ul2);
474
66
        break;
475
57
    case 0xc0:
476
57
        GPSD_LOG(LOG_INF, &session->context->errout,
477
57
                 "Response to Change operation mode command\n");
478
57
        break;
479
67
    case 0xc1:
480
67
        ul4 = getleu32(buf, OFFSET(1));
481
67
        ul1 = getleu32(buf, OFFSET(2));
482
67
        ul2 = getleu32(buf, OFFSET(3));
483
67
        ul3 = getleu32(buf, OFFSET(4));
484
67
        (void)snprintf(session->subtype, sizeof(session->subtype),
485
67
                       "%u.%u %u.%u.%u %x %c-%u\n",
486
67
                       ul4>>16, ul4&0xFFFF, ul1>>9, (ul1>>5)&0xF,
487
67
                       ul1&0x1F, ul2, ul3>>24, ul3&0x00FFFFFF);
488
67
        GPSD_LOG(LOG_INF, &session->context->errout,
489
67
                 "Response to Request FW version command: %s\n",
490
67
                 session->subtype);
491
67
        mask |= DEVICEID_SET;
492
67
        break;
493
129
    case 0xc2:
494
129
        GPSD_LOG(LOG_INF, &session->context->errout,
495
129
                 "Response to Restart receiver command\n");
496
129
        break;
497
45
    case 0xc3:
498
45
        GPSD_LOG(LOG_INF, &session->context->errout,
499
45
                 "Response to Store parameters to Flash command\n");
500
45
        break;
501
109
    case 0xd0:
502
109
        GPSD_LOG(LOG_INF, &session->context->errout,
503
109
                 "Response to Erase Flash sector command\n");
504
109
        break;
505
74
    case 0xd1:
506
74
        GPSD_LOG(LOG_INF, &session->context->errout,
507
74
                 "Response to Write data to Flash command\n");
508
74
        break;
509
97
    case 0xd2:
510
97
        GPSD_LOG(LOG_INF, &session->context->errout,
511
97
                 "Response to Store Serial Number command\n");
512
97
        break;
513
152
    default:
514
152
        GPSD_LOG(LOG_WARN, &session->context->errout,
515
152
                 "Unhandled GeoStar packet type 0x%02x\n", id);
516
152
        break;
517
4.33k
    }
518
519
4.33k
    return mask;
520
4.33k
}
521
522
static gps_mask_t geostar_parse_input(struct gps_device_t *session)
523
4.57k
{
524
4.57k
    if (GEOSTAR_PACKET == session->lexer.type) {
525
4.40k
        return geostar_analyze(session);
526
4.40k
    }   // else
527
164
    return 0;
528
4.57k
}
529
530
// not used by the daemon, it's for gpsctl and friends
531
static ssize_t geostar_control_send(struct gps_device_t *session,
532
                                 char *buf, size_t buflen)
533
0
{
534
0
    return (ssize_t)geostar_write(session,
535
0
                                  (unsigned int)buf[0],
536
0
                                  (unsigned char *)buf + 1,
537
0
                                  (buflen - 1) / 4);
538
0
}
539
540
541
static void geostar_init_query(struct gps_device_t *session)
542
134
{
543
134
    unsigned char buf[4];
544
545
    // 0xC1 request content is ignored, init for Coverity
546
134
    memset(buf, 0, sizeof(buf));
547
548
    // Request Firmware Version
549
134
    (void)geostar_write(session, 0xc1, buf, 1);
550
134
}
551
552
static void geostar_event_hook(struct gps_device_t *session, event_t event)
553
7.00k
{
554
7.00k
    unsigned char buf[2 * 4];
555
556
7.00k
    if (session->context->readonly ||
557
5.02k
        session->context->passive) {
558
4.12k
        return;
559
4.12k
    }
560
561
2.88k
    if (EVENT_IDENTIFIED == event ||
562
2.83k
        EVENT_REACTIVATE == event) {
563
        // Select binary packets
564
48
        putbe32(buf, 0, 0xffff0000);
565
48
        putbe32(buf, 4, 0);
566
48
        (void)geostar_write(session, 0x4f, buf, 2);
567
568
        // Poll Ports params
569
48
        putbe32(buf, 0, 1);
570
48
        (void)geostar_write(session, 0x81, buf, 1);
571
48
        putbe32(buf, 0, 0);
572
48
        (void)geostar_write(session, 0x81, buf, 1);
573
        // Poll Init params
574
48
        (void)geostar_write(session, 0x80, buf, 1);
575
        // Poll Mode
576
48
        (void)geostar_write(session, 0x82, buf, 1);
577
        // Poll Solution params
578
48
        (void)geostar_write(session, 0x83, buf, 1);
579
        // Poll Output rate
580
48
        (void)geostar_write(session, 0x84, buf, 1);
581
        // Poll Protocols assignment
582
48
        (void)geostar_write(session, 0x86, buf, 1);
583
        // Poll PPS params
584
48
        (void)geostar_write(session, 0x8c, buf, 1);
585
        // Poll NMEA packets selected
586
48
        (void)geostar_write(session, 0x8e, buf, 1);
587
        // Poll binary packets selected
588
48
        (void)geostar_write(session, 0x8f, buf, 1);
589
48
    }
590
591
2.88k
    if (EVENT_DEACTIVATE == event) {
592
        // Perform cold restart.  Seem brutal??
593
0
        putbe32(buf, 0, 3);
594
0
        (void)geostar_write(session, 0xc2, buf, 1);
595
0
    }
596
2.88k
}
597
598
static bool geostar_speed_switch(struct gps_device_t *session,
599
                                 speed_t speed, char parity, int stopbits)
600
0
{
601
0
    unsigned char buf[4 * 4];
602
0
    int iparity;
603
604
0
    switch (parity) {
605
0
    case 'E':
606
0
    case 2:
607
0
        parity = (char)2;
608
0
        break;
609
0
    case 'O':
610
0
    case 1:
611
0
        parity = (char)1;
612
0
        break;
613
0
    case 'N':
614
0
    case 0:
615
0
    default:
616
0
        parity = (char)0;
617
0
        break;
618
0
    }
619
0
    iparity = parity;
620
621
0
    putbe32(buf, 0, session->driver.geostar.physical_port);
622
0
    putbe32(buf, 4, speed);
623
0
    putbe32(buf, 8, stopbits);
624
0
    putbe32(buf, 12, iparity);
625
0
    (void)geostar_write(session, 0x41, buf, 4);
626
627
0
    return true;        // it would be nice to error-check this
628
0
}
629
630
static void geostar_mode(struct gps_device_t *session, int mode)
631
0
{
632
0
    if (MODE_NMEA == mode) {
633
0
        unsigned char buf[1 * 4];
634
        // Switch to NMEA mode
635
0
        putbe32(buf, 0, 1);
636
0
        (void)geostar_write(session, 0x46, buf, 1);
637
0
    } else if (MODE_BINARY == mode) {
638
        // Switch to binary mode
639
0
        (void)nmea_send(session, "$GPSGG,SWPROT");
640
0
    } else {
641
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
642
0
                 "unknown mode %i requested\n", mode);
643
0
    }
644
0
}
645
646
static double geostar_time_offset(struct gps_device_t *session UNUSED)
647
0
{
648
0
    return 0.31;
649
0
}
650
651
// this is everything we export
652
// *INDENT-OFF*
653
const struct gps_type_t driver_geostar =
654
{
655
    .type_name      = "GeoStar",             // full name of type
656
    .packet_type    = GEOSTAR_PACKET,        // associated lexer packet type
657
    .flags          = DRIVER_STICKY,         // remember this
658
    .trigger        = NULL,                  // no trigger
659
    .channels       = GEOSTAR_CHANNELS,      // consumer-grade GPS/GLONASS
660
    .probe_detect   = geostar_detect,        // probe for device
661
    .get_packet     = packet_get1,           // use the generic packet getter
662
    .parse_packet   = geostar_parse_input,   // parse message packets
663
    .rtcm_writer    = NULL,                  // no DGPS corrections
664
    .init_query     = geostar_init_query,    // non-perturbing initial query
665
    .event_hook     = geostar_event_hook,    // fire on various lifetime events
666
    .speed_switcher = geostar_speed_switch,  // change baud rate
667
    .mode_switcher  = geostar_mode,          // there is a mode switcher
668
    .rate_switcher  = NULL,                  // no rate switcher
669
    .min_cycle.tv_sec  = 1,                  // not relevant, no rate switch
670
    .min_cycle.tv_nsec = 0,                  // not relevant, no rate switch
671
    .control_send   = geostar_control_send,  // how to send commands
672
    .time_offset     = geostar_time_offset,
673
};
674
// *INDENT-ON*
675
676
#endif  // GEOSTAR_ENABLE
677
678
// vim: set expandtab shiftwidth=4