Coverage Report

Created: 2025-03-14 06:43

/src/gpsd/gpsd-3.25.1~dev/drivers/driver_evermore.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * DEPRECATED September 2019
3
 *
4
 * September 2019: Looks like Everymore converted most of their products
5
 * over to using to SiRF and u-blox chips a long time ago.  They still offer
6
 * the EB-E26 and EB-E36-5Hz modules, but these appear to be NMEA only.  Their
7
 * web site has not documentation: http://www.evermoregps.com.tw/
8
 *
9
 * May 2013: The binary bits were commented out.  Then removed in
10
 * September 2019, but will live forever in the git history...
11
 *
12
 * This is the gpsd driver for EverMore GPSes.  They have both an NMEA and
13
 * a binary reporting mode, with the interesting property that they will
14
 * cheerfully accept binary commands (such as speed changes) while in NMEA
15
 * mode.
16
 *
17
 * This driver seems to be a subset of driver_sirf.c.  Is it needed at all?
18
 *
19
 * Binary mode would give us atomic fix reports, but it has one large drawback:
20
 * the Navigation Data Out message doesn't report a leap-second offset, so it
21
 * is not actually possible to collect a leap-second offset from it. Therefore
22
 * we'll normally run the driver in NMEA mode.
23
 *
24
 * About the only thing binary mode gives that NMEA won't is TDOP and raw
25
 * pseudoranges, but gpsd does its own DOPs from skyview. By default we'll
26
 * trade away raw data to get accurate time.
27
 *
28
 * The vendor site is <http://www.emt.com.tw>.
29
 *
30
 * This driver was written by Petr Slansky based on a framework by Eric S.
31
 * Raymond.  The following remarks are by Petr Slansky.
32
 *
33
 * Snooping on the serial the communication between a Windows program and
34
 * an Evermore chipset reveals some messages not described in the vendor
35
 * documentation (Issue C of Aug 2002):
36
 *
37
 * 10 02 06 84 00 00 00 84 10 03        switch to binary mode (84 00 00 00)
38
 * 10 02 06 84 01 00 00 85 10 03        switch to NMEA mode (84 01 00 00)
39
 *
40
 * 10 02 06 89 01 00 00 8a 10 03        set baud rate 4800
41
 * 10 02 06 89 01 01 00 8b 10 03        set baud rate 9600
42
 * 10 02 06 89 01 02 00 8c 10 03        set baud rate 19200
43
 * 10 02 06 89 01 03 00 8d 10 03        set baud rate 38400
44
 *
45
 * 10 02 06 8D 00 01 00 8E 10 03        switch to datum ID 001 (WGS-84)
46
 * 10 02 06 8D 00 D8 00 65 10 03        switch to datum ID 217 (WGS-72)
47
 *
48
 * These don't entail a reset of GPS as the 0x80 message does.
49
 *
50
 * 10 02 04 38 85 bd 10 03     answer from GPS to 0x85 message; ACK message
51
 * 10 02 04 38 8d c5 10 03     answer from GPS to 0x8d message; ACK message
52
 * 10 02 04 38 8e c6 10 03     answer from GPS to 0x8e message; ACK message
53
 * 10 02 04 38 8f c7 10 03     answer from GPS to 0x8f message; ACK message
54
 *
55
 * The chip sometimes sends vendor extension messages with the prefix
56
 * $PEMT,100. After restart, it sends a $PEMT,100 message describing the
57
 * chip's configuration. Here is a sample:
58
 *
59
 * $PEMT,100,05.42g,100303,180,05,1,20,15,08,0,0,2,1*5A
60
 * 100 - message type
61
 * 05.42g - firmware version
62
 * 100303 - date of firmware release DDMMYY
63
 * 180 -  datum ID; 001 is WGS-84
64
 * 05 - default elevation mask; see message 0x86
65
 * 1 - default DOP select, 1 is auto DOP mask; see message 0x87
66
 * 20 - default GDOP; see message 0x87
67
 * 15 - default PDOP
68
 * 08 - default HDOP
69
 * 0 - Normal mode, without 1PPS
70
 * 0 - default position pinning control (0 disable, 1 enable)
71
 * 2 - altitude hold mode (0 disable, 1 always, 2 auto)
72
 * 1 - 2/1 satellite nav mode (0,1,2,3,4)
73
 *          0 disable 2/1 sat nav mode
74
 *          1 hold direction (2 sat)
75
 *          2 clock hold only (2 sat)
76
 *          3 direction hold then clock hold (1 sat)
77
 *          4 clock hold then direction hold (1 sat)
78
 *
79
 * Message $PEMT,100 could be forced with message 0x85 (restart):
80
 * 10 02 12 85 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 87 10 03
81
 * 0x85 ID, Restart
82
 * 0x00 restart mode (0 default, 1 hot, 2 warm, 3 cold, 4 test)
83
 * 0x00 test start search PRN (1-32)
84
 * 0x00 UTC second (0-59)
85
 * 0x00 UTC Minute (0-59)
86
 * 0x00 UTC Hour (0-23)
87
 * 0x01 UTC Day (1-31)
88
 * 0x01 UTC Month (1-12)
89
 * 0x0000 UTC year (1980+x, uint16)
90
 * 0x0000 Latitude WGS-84 (+/-900, 1/10 degree, + for N, int16)
91
 * 0x0000 Longtitude WGS-84 (+/-1800, 1/10 degree, + for E, int16)
92
 * 0x0000 Altitude WGS-84 (-1000..+18000, meters, int16)
93
 * 0x87 CRC
94
 *
95
 * With message 0x8e it is possible to define how often each NMEA
96
 * message is sent (0-255 seconds). It is possible with message 0x8e
97
 * to activate PEMT,101 messages that have information about time,
98
 * position, velocity and HDOP.
99
 *
100
 * $PEMT,101,1,02,00.0,300906190446,5002.5062,N,01427.6166,E,00259,000,0000*27
101
 * $PEMT,101,2,06,02.1,300906185730,5002.7546,N,01426.9524,E,00323,020,0011*26
102
 * 101 - message type, Compact Navigation Solution
103
 * 2 - position status (1,2,3,4,5,6)
104
 *      (1 invalid, 2 2D fix, 3 3D fix, 4 2D with DIFF, 5 3D with DIFF,
105
 *       6 2/1 sat degrade mode)
106
 * 06 - number of used satellites
107
 * 02.1 - DOP (00.0 no fix, HDOP 2D fix, PDOP 3D fix)
108
 * 300906185730 - date and time, UTC ddmmyyHHMMSS (30/09/2006 18:57:30)
109
 * 5002.7546,N - Latitude (degree)
110
 * 01426.9524,E - Longitude (degree)
111
 * 00323 - Altitude (323 metres)
112
 * 020 - heading (20 degrees from true north)
113
 * 0011 - speed over ground (11 metres per second); documentation says km per h
114
 *
115
 * This is an exampe of an 0x8e message that activates all NMEA sentences
116
 * with 1s period:
117
 * 10 02 12 8E 7F 01 01 01 01 01 01 01 01 00 00 00 00 00 00 15 10 03
118
 *
119
 * There is a way to probe for this chipset. When binary message 0x81 is sent:
120
 * 10 02 04 81 13 94 10 03
121
 *
122
 * EverMore will reply with message like this:
123
 * *10 *02 *0D *20 E1 00 00 *00 0A 00 1E 00 32 00 5B *10 *03
124
 * bytes marked with * are fixed
125
 * Message in reply is information about logging configuration of GPS
126
 *
127
 * Another way to detect the EverMore chipset is to send one of the messages
128
 * 0x85, 0x8d, 0x8e or 0x8f and check for a reply.
129
 * The reply message from an EverMore GPS will look like this:
130
 * *10 *02 *04 *38 8d c5 *10 *03
131
 * 8d indicates that message 0x8d was sent;
132
 * c5 is EverMore checksum, other bytes are fixed
133
 *
134
 * This file is Copyright 2010 by the GPSD project
135
 * SPDX-License-Identifier: BSD-2-clause
136
 */
137
138
#include "../include/gpsd_config.h"  // must be before all includes
139
140
#include <math.h>
141
#include <stdbool.h>
142
#include <stdio.h>
143
#include <string.h>
144
145
#include "../include/gpsd.h"
146
#if defined(EVERMORE_ENABLE)
147
148
#include "../include/bits.h"
149
150
#define EVERMORE_CHANNELS       12
151
152
static ssize_t evermore_control_send(struct gps_device_t *session, char *buf,
153
                                     size_t len)
154
0
{
155
0
    unsigned int crc;
156
0
    size_t i;
157
0
    char *cp;
158
159
    // prepare a DLE-stuffed copy of the message
160
0
    cp = session->msgbuf;
161
0
    *cp++ = 0x10;               // message starts with DLE STX
162
0
    *cp++ = 0x02;
163
164
0
    session->msgbuflen = (size_t) (len + 2);    // len < 254 !!
165
0
    *cp++ = (char)session->msgbuflen;   // message length
166
0
    if (session->msgbuflen == 0x10)
167
0
        *cp++ = 0x10;
168
169
    // payload
170
0
    crc = 0;
171
0
    for (i = 0; i < len; i++) {
172
0
        *cp++ = buf[i];
173
0
        if (buf[i] == 0x10)
174
0
            *cp++ = 0x10;
175
0
        crc += buf[i];
176
0
    }
177
178
0
    crc &= 0xff;
179
180
    // enter CRC after payload
181
0
    *cp++ = crc;
182
0
    if (crc == 0x10)
183
0
        *cp++ = 0x10;
184
185
0
    *cp++ = 0x10;               // message ends with DLE ETX
186
0
    *cp++ = 0x03;
187
188
0
    session->msgbuflen = (size_t) (cp - session->msgbuf);
189
190
0
    return gpsd_write(session, session->msgbuf, session->msgbuflen);
191
0
}
192
193
194
static bool evermore_protocol(struct gps_device_t *session, int protocol)
195
0
{
196
0
    char tmp8;
197
0
    char evrm_protocol_config[] = {
198
0
        (char)0x84,             // 0: msg ID, Protocol Configuration
199
0
        (char)0x00,             // 1: mode; EverMore binary(0), NMEA(1)
200
0
        (char)0x00,             // 2: reserved
201
0
        (char)0x00,             // 3: reserved
202
0
    };
203
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
204
0
             "evermore_protocol(%d)\n", protocol);
205
0
    tmp8 = (protocol != 0) ? 1 : 0;
206
    // NMEA : binary
207
0
    evrm_protocol_config[1] = tmp8;
208
0
    return (evermore_control_send
209
0
            (session, evrm_protocol_config,
210
0
             sizeof(evrm_protocol_config)) != -1);
211
0
}
212
213
/* mode = 0 : EverMore default
214
 * mode = 1 : gpsd best
215
 * mode = 2 : EverMore search, activate PEMT101 message */
216
static bool evermore_nmea_config(struct gps_device_t *session, int mode)
217
0
{
218
0
    unsigned char tmp8;
219
0
    unsigned char evrm_nmeaout_config[] = {
220
0
        0x8e,  // 0: msg ID, NMEA Message Control
221
0
        0xff,  // 1: NMEA sentence bitmask, GGA(0), GLL(1), GSA(2), GSV(3)...
222
0
        0x01,  // 2: nmea checksum no(0), yes(1)
223
0
        1,     // 3: GPGGA, interval 0-255s
224
0
        0,     // 4: GPGLL, interval 0-255s
225
0
        1,     // 5: GPGSA, interval 0-255s
226
0
        1,     // 6: GPGSV, interval 0-255s
227
0
        1,     // 7: GPRMC, interval 0-255s
228
0
        0,     // 8: GPVTG, interval 0-255s
229
0
        0,     // 9: PEMT,101, interval 0-255s
230
0
        0, 0, 0, 0, 0, 0,       // 10-15: reserved
231
0
    };
232
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
233
0
             "evermore_nmea_config(%d)\n", mode);
234
0
    tmp8 = (mode == 1) ? 5 : 1;
235
    // NMEA GPGSV, gpsd
236
0
    evrm_nmeaout_config[6] = tmp8;      // GPGSV, 1s or 5s
237
0
    tmp8 = (mode == 2) ? 1 : 0;
238
    // NMEA PEMT101
239
0
    evrm_nmeaout_config[9] = tmp8;      // PEMT101, 1s or 0s
240
0
    return (evermore_control_send(session, (char *)evrm_nmeaout_config,
241
0
                                  sizeof(evrm_nmeaout_config)) != -1);
242
0
}
243
244
static void evermore_mode(struct gps_device_t *session, int mode)
245
0
{
246
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
247
0
             "evermore_mode(%d)\n", mode);
248
0
    if (mode == MODE_NMEA) {
249
        // NMEA
250
0
        (void)evermore_protocol(session, 1);
251
        // configure NMEA messages for gpsd
252
0
        (void)evermore_nmea_config(session, 1);
253
0
    } else {
254
        // binary
255
0
        (void)evermore_protocol(session, 0);
256
0
    }
257
0
}
258
259
static void evermore_event_hook(struct gps_device_t *session, event_t event)
260
0
{
261
0
    if (session->context->readonly ||
262
0
        session->context->passive) {
263
0
        return;
264
0
    }
265
266
    /*
267
     * FIX-ME: It might not be necessary to call this on reactivate.
268
     * Experiment to see if the holds its settings through a close.
269
     */
270
0
    if (event == EVENT_IDENTIFIED ||
271
0
        event == EVENT_REACTIVATE) {
272
        /*
273
         * We used to run this driver in binary mode, but that has the
274
         * problem that Evermore binary mode doesn't report a
275
         * leap-second correction in the Navigation Data Out sentence.
276
         * So, run it in NMEA mode to getbUTC corrected by firmware.
277
         * Fortunately the Evermore firmware interprets binary
278
         * commands in NMEA mode, so nothing else needs to change.
279
         */
280
0
        (void)evermore_mode(session, 0);        // switch GPS to NMEA mode
281
        // configure NMEA messages for gpsd (GPGSV every 5s)
282
0
        (void)evermore_nmea_config(session, 1);
283
0
    } else if (event == EVENT_DEACTIVATE) {
284
        // configure NMEA messages to default
285
0
        (void)evermore_nmea_config(session, 0);
286
0
    }
287
0
}
288
289
static bool evermore_speed(struct gps_device_t *session,
290
                           speed_t speed, char parity, int stopbits)
291
0
{
292
0
    GPSD_LOG(LOG_PROG, &session->context->errout,
293
0
             "evermore_speed(%u%c%d)\n", (unsigned int)speed, parity,
294
0
             stopbits);
295
    // parity and stopbit switching aren't available on this chip
296
0
    if (parity != session->gpsdata.dev.parity
297
0
        || stopbits != (int)session->gpsdata.dev.stopbits) {
298
0
        return false;
299
0
    } else {
300
0
        unsigned char tmp8;
301
0
        unsigned char msg[] = {
302
0
            0x89, // 0: msg ID, Serial Port Configuration
303
0
            0x01, // 1: bit 0 cfg for main serial, bit 1 cfg for DGPS port
304
0
            0x00, /* 2: main serial baud rate; 4800(0), 9600(1), 19200(2),
305
                   *    38400(3) */
306
0
            0x00, // 3: baud rate for DGPS serial port; 4800(0), 9600(1), etc
307
0
        };
308
0
        switch (speed) {
309
0
        case 4800:
310
0
            tmp8 = 0;
311
0
            break;
312
0
        case 9600:
313
0
            tmp8 = 1;
314
0
            break;
315
0
        case 19200:
316
0
            tmp8 = 2;
317
0
            break;
318
0
        case 38400:
319
0
            tmp8 = 3;
320
0
            break;
321
0
        default:
322
0
            return false;
323
0
        }
324
0
        msg[2] = tmp8;
325
0
        return (evermore_control_send(session, (char *)msg, sizeof(msg)) !=
326
0
                -1);
327
0
    }
328
0
}
329
330
// change the sample rate of the GPS
331
static bool evermore_rate_switcher(struct gps_device_t *session, double rate)
332
0
{
333
0
    if (rate < 1 || rate > 10) {
334
0
        GPSD_LOG(LOG_ERROR, &session->context->errout,
335
0
                 "valid rate range is 1-10.\n");
336
0
        return false;
337
0
    } else {
338
0
        unsigned char evrm_rate_config[] = {
339
0
            0x84,               // 1: msg ID, Operating Mode Configuration
340
0
            0x02,               // 2: normal mode with 1PPS
341
0
            0x00,               // 3: navigation update rate
342
0
            0x00,               // 4: RF/GPSBBP On Time
343
0
        };
344
0
        evrm_rate_config[2] = (unsigned char)trunc(rate);
345
0
        return (evermore_control_send(session, (char *)evrm_rate_config,
346
0
                                      sizeof(evrm_rate_config)) != -1);
347
0
    }
348
0
}
349
350
351
// this is everything we export
352
// *INDENT-OFF*
353
const struct gps_type_t driver_evermore =
354
{
355
    .type_name      = "EverMore",               // full name of type
356
    .packet_type    = EVERMORE_PACKET,          // lexer packet type
357
    .flags          = DRIVER_STICKY,            // remember this
358
    .trigger        = NULL,                     // recognize the type
359
    .channels       = EVERMORE_CHANNELS,        // consumer-grade GPS
360
    .probe_detect   = NULL,                     // no probe
361
    .get_packet     = packet_get1,              // use generic one
362
    .parse_packet   = generic_parse_input,      // parse message packets
363
    .rtcm_writer    = gpsd_write,               // send RTCM data straight
364
    .init_query     = NULL,                     // non-perturbing query
365
    .event_hook     = evermore_event_hook,      // lifetime event handler
366
    .speed_switcher = evermore_speed,           // we can change baud rates
367
    .mode_switcher  = evermore_mode,            // there is a mode switcher
368
    .rate_switcher  = evermore_rate_switcher,   // change sample rate
369
    .min_cycle.tv_sec  = 1,             // not relevant, no rate switch
370
    .min_cycle.tv_nsec = 0,             // not relevant, no rate switch
371
    .control_send   = evermore_control_send,    // how to send a control string
372
    .time_offset     = NULL,            // no method for NTP fudge factor
373
};
374
// *INDENT-ON*
375
#endif  // defined(EVERMORE_ENABLE)
376
// vim: set expandtab shiftwidth=4