/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 |