cleanup specfile for packaging
[profile/ivi/gpsd.git] / driver_evermore.c
1 /*
2  *
3  * This is the gpsd driver for EverMore GPSes operating in binary mode.
4  * About the only thing this gives us that NMEA won't is TDOP.
5  * But we'll get atomic position reports from it, which is good.
6  *
7  * The vendor site is <http://www.emt.com.tw>.
8  *
9  * This driver was written by Petr Slansky based on a framework by Eric S.
10  * Raymond.  The following remarks are by Petr Slansky.
11  *
12  * Snooping on the serial the communication between a Windows program and
13  * an Evermore chipset reveals some messages not described in the vendor
14  * documentation (Issue C of Aug 2002):
15  *
16  * 10 02 06 84 00 00 00 84 10 03        switch to binary mode (84 00 00 00)
17  * 10 02 06 84 01 00 00 85 10 03        switch to NMEA mode (84 01 00 00)
18  *
19  * 10 02 06 89 01 00 00 8a 10 03        set baud rate 4800
20  * 10 02 06 89 01 01 00 8b 10 03        set baud rate 9600
21  * 10 02 06 89 01 02 00 8c 10 03        set baud rate 19200
22  * 10 02 06 89 01 03 00 8d 10 03        set baud rate 38400
23  *
24  * 10 02 06 8D 00 01 00 8E 10 03        switch to datum ID 001 (WGS-84)
25  * 10 02 06 8D 00 D8 00 65 10 03        switch to datum ID 217 (WGS-72)
26  *
27  * These don't entail a reset of GPS as the 0x80 message does.
28  *
29  * 10 02 04 38 85 bd 10 03     answer from GPS to 0x85 message; ACK message
30  * 10 02 04 38 8d c5 10 03     answer from GPS to 0x8d message; ACK message
31  * 10 02 04 38 8e c6 10 03     answer from GPS to 0x8e message; ACK message
32  * 10 02 04 38 8f c7 10 03     answer from GPS to 0x8f message; ACK message
33  *
34  * The chip sometimes sends vendor extension messages with the prefix
35  * $PEMT,100. After restart, it sends a $PEMT,100 message describing the
36  * chip's configuration. Here is a sample:
37  *
38  * $PEMT,100,05.42g,100303,180,05,1,20,15,08,0,0,2,1*5A
39  * 100 - message type
40  * 05.42g - firmware version
41  * 100303 - date of firmware release DDMMYY
42  * 180 -  datum ID; 001 is WGS-84
43  * 05 - default elevation mask; see message 0x86
44  * 1 - default DOP select (1 is auto DOP mask); see message 0x87
45  * 20 - default GDOP; see message 0x87
46  * 15 - default PDOP
47  * 08 - default HDOP
48  * 0 - Normal mode, without 1PPS
49  * 0 - default position pinning control (0 disable, 1 enable)
50  * 2 - altitude hold mode (0 disable, 1 always, 2 auto)
51  * 1 - 2/1 satellite nav mode (0,1,2,3,4)
52  *          0 disable 2/1 sat nav mode
53  *          1 hold direction (2 sat)
54  *          2 clock hold only (2 sat)
55  *          3 direction hold then clock hold (1 sat)
56  *          4 clock hold then direction hold (1 sat)
57  *
58  * Message $PEMT,100 could be forced with message 0x85 (restart):
59  * 10 02 12 85 00 00 00 00 00 01 01 00 00 00 00 00 00 00 00 87 10 03
60  * 0x85 ID, Restart
61  * 0x00 restart mode (0 default, 1 hot, 2 warm, 3 cold, 4 test)
62  * 0x00 test start search PRN (1-32)
63  * 0x00 UTC second (0-59)
64  * 0x00 UTC Minute (0-59)
65  * 0x00 UTC Hour (0-23)
66  * 0x01 UTC Day (1-31)
67  * 0x01 UTC Month (1-12)
68  * 0x0000 UTC year (1980+x, uint16)
69  * 0x0000 Latitude WGS-84 (+/-900, 1/10 degree, + for N, int16)
70  * 0x0000 Longtitude WGS-84 (+/-1800, 1/10 degree, + for E, int16)
71  * 0x0000 Altitude WGS-84 (-1000..+18000, meters, int16)
72  * 0x87 CRC
73  *
74  * With message 0x8e it is possible to define how often each NMEA
75  * message is sent (0-255 seconds). It is possible with message 0x8e
76  * to activate PEMT,101 messages that have information about time,
77  * position, velocity and HDOP.
78  *
79  * $PEMT,101,1,02,00.0,300906190446,5002.5062,N,01427.6166,E,00259,000,0000*27
80  * $PEMT,101,2,06,02.1,300906185730,5002.7546,N,01426.9524,E,00323,020,0011*26
81  * 101 - message type, Compact Navigation Solution
82  * 2 - position status (1,2,3,4,5,6)
83  *      (1 invalid, 2 2D fix, 3 3D fix, 4 2D with DIFF, 5 3D with DIFF,
84  *       6 2/1 sat degrade mode)
85  * 06 - number of used satelites
86  * 02.1 - DOP (00.0 no fix, HDOP 2D fix, PDOP 3D fix)
87  * 300906185730 - date and time, UTC ddmmyyHHMMSS (30/09/2006 18:57:30)
88  * 5002.7546,N - Latitude (degree)
89  * 01426.9524,E - Longitude (degree)
90  * 00323 - Altitude (323 metres)
91  * 020 - heading (20 degrees from true north)
92  * 0011 - speed over ground (11 metres per second); documentation says km per h
93  *
94  * This is an exampe of an 0x8e message that activates all NMEA sentences
95  * with 1s period:
96  * 10 02 12 8E 7F 01 01 01 01 01 01 01 01 00 00 00 00 00 00 15 10 03
97  *
98  * There is a way to probe for this chipset. When binary message 0x81 is sent:
99  * 10 02 04 81 13 94 10 03
100  *
101  * EverMore will reply with message like this:
102  * *10 *02 *0D *20 E1 00 00 *00 0A 00 1E 00 32 00 5B *10 *03
103  * bytes marked with * are fixed
104  * Message in reply is information about logging configuration of GPS
105  *
106  * Another way to detect the EverMore chipset is to send one of the messages
107  * 0x85, 0x8d, 0x8e or 0x8f and check for a reply.
108  * The reply message from an EverMore GPS will look like this:
109  * *10 *02 *04 *38 8d c5 *10 *03
110  * 8d indicates that message 0x8d was sent;
111  * c5 is EverMore checksum, other bytes are fixed
112  *
113  * This file is Copyright (c) 2010 by the GPSD project
114  * BSD terms apply: see the file COPYING in the distribution root for details.
115  */
116
117 #include <sys/types.h>
118 #include <stdio.h>
119 #include <stdlib.h>
120 #include <string.h>
121 #include <math.h>
122 #include <ctype.h>
123 #ifndef S_SPLINT_S
124 #include <unistd.h>
125 #endif /* S_SPLINT_S */
126 #include <time.h>
127 #include <stdio.h>
128
129 #include "gpsd.h"
130 #if defined(EVERMORE_ENABLE) && defined(BINARY_ENABLE)
131
132 #define GET_ORIGIN 1
133 #define PUT_ORIGIN 0
134 #include "bits.h"
135
136 #define EVERMORE_CHANNELS       12
137
138 /*@ +charint @*/
139 gps_mask_t evermore_parse(struct gps_device_t * session, unsigned char *buf,
140                           size_t len)
141 {
142     unsigned char buf2[MAX_PACKET_LENGTH], *cp, *tp;
143     size_t i, datalen;
144     unsigned int type, used, visible, satcnt, j, k;
145     double version;
146     gps_mask_t mask = 0;
147
148     if (len == 0)
149         return 0;
150
151     /* time to unstuff it and discard the header and footer */
152     cp = buf + 2;
153     tp = buf2;
154     if (*cp == 0x10)
155         cp++;
156     datalen = (size_t) * cp++;
157
158     gpsd_report(LOG_RAW, "raw EverMore packet type 0x%02x, length %zd: %s\n",
159                 *cp, len, gpsd_hexdump_wrapper(buf, len, LOG_RAW));
160
161     datalen -= 2;
162
163     /*@ -usedef @*/
164     for (i = 0; i < (size_t) datalen; i++) {
165         *tp = *cp++;
166         if (*tp == 0x10)
167             cp++;
168         tp++;
169     }
170
171     type = (unsigned char)getub(buf2, 1);
172     /*@ +usedef @*/
173
174     /*@ -usedef -compdef @*/
175     gpsd_report(LOG_RAW, "EverMore packet type 0x%02x, length %zd: %s\n",
176                 type, datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_RAW));
177     /*@ +usedef +compdef @*/
178
179     (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
180                    "EID%u", type);
181
182     session->cycle_end_reliable = true;
183
184     switch (type) {
185     case 0x02:                  /* Navigation Data Output */
186         session->context->gps_week = (unsigned short)getleuw(buf2, 2);
187         session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
188         /*@ ignore @*//*@ splint is confused @ */
189         session->newdata.time =
190             gpstime_to_unix(session->context->gps_week,
191                             session->context->gps_tow) -
192             session->context->leap_seconds;
193         /*@ end @*/
194         ecef_to_wgs84fix(&session->newdata, &session->gpsdata.separation,
195                          getlesl(buf2, 8) * 1.0, getlesl(buf2, 12) * 1.0,
196                          getlesl(buf2, 16) * 1.0, getlesw(buf2, 20) / 10.0,
197                          getlesw(buf2, 22) / 10.0, getlesw(buf2, 24) / 10.0);
198         used = (unsigned char)getub(buf2, 26) & 0x0f;
199         //visible = (getub(buf2, 26) & 0xf0) >> 4;
200         version = (uint) getleuw(buf2, 27) / 100.0;
201         /* that's all the information in this packet */
202         if (used < 3)
203             session->newdata.mode = MODE_NO_FIX;
204         else if (used == 3)
205             session->newdata.mode = MODE_2D;
206         else {
207             session->newdata.mode = MODE_3D;
208             mask |= ALTITUDE_IS | CLIMB_IS;
209         }
210         mask |= TIME_IS | LATLON_IS | TRACK_IS | SPEED_IS | MODE_IS;
211         if (session->subtype[0] == '\0') {
212             (void)snprintf(session->subtype, sizeof(session->subtype),
213                            "%3.2f", version);
214             mask |= DEVICEID_IS;
215         }
216         gpsd_report(LOG_DATA,
217                     "NDO 0x02: time=%.2f, lat=%.2f lon=%.2f alt=%.2f speed=%.2f track=%.2f climb=%.2f mode=%d subtype='%s' mask=%s\n",
218                     session->newdata.time, session->newdata.latitude,
219                     session->newdata.longitude, session->newdata.altitude,
220                     session->newdata.speed, session->newdata.track,
221                     session->newdata.climb, session->newdata.mode,
222                     session->gpsdata.dev.subtype, gpsd_maskdump(mask));
223         return mask | CLEAR_IS | REPORT_IS;
224
225     case 0x04:                  /* DOP Data Output */
226         session->context->gps_week = (unsigned short)getleuw(buf2, 2);
227         session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
228         /*@ ignore @*//*@ splint is confused @ */
229         session->newdata.time =
230             gpstime_to_unix(session->context->gps_week,
231                             session->context->gps_tow) -
232             session->context->leap_seconds;
233         /*@ end @*/
234         /*
235          * We make a deliberate choice not to clear DOPs from the
236          * last skyview here, but rather to treat this as a supplement
237          * to our calculations from the visiniolity matrix, trusting
238          * the firmware algorithms over ours.
239          */
240         session->gpsdata.dop.gdop = (double)getub(buf2, 8) * 0.1;
241         session->gpsdata.dop.pdop = (double)getub(buf2, 9) * 0.1;
242         session->gpsdata.dop.hdop = (double)getub(buf2, 10) * 0.1;
243         session->gpsdata.dop.vdop = (double)getub(buf2, 11) * 0.1;
244         session->gpsdata.dop.tdop = (double)getub(buf2, 12) * 0.1;
245         switch (getub(buf2, 13)) {
246         case 0:         /* no position fix */
247         case 1:         /* manual calls this "1D navigation" */
248             session->gpsdata.status = STATUS_NO_FIX;
249             session->newdata.mode = MODE_NO_FIX;
250             break;
251         case 2:         /* 2D navigation */
252             session->gpsdata.status = STATUS_FIX;
253             session->newdata.mode = MODE_2D;
254             break;
255         case 3:         /* 3D navigation */
256             session->gpsdata.status = STATUS_FIX;
257             session->newdata.mode = MODE_3D;
258             break;
259         case 4:         /* 3D navigation with DGPS */
260             session->gpsdata.status = STATUS_DGPS_FIX;
261             session->newdata.mode = MODE_3D;
262             break;
263         }
264         /* that's all the information in this packet */
265         mask = TIME_IS | DOP_IS | MODE_IS | STATUS_IS;
266         gpsd_report(LOG_DATA,
267                     "DDO 0x04: gdop=%.2f pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f mode=%d, status=%d mask={TIME| DOP|MODE|STATUS}\n",
268                     session->gpsdata.dop.gdop, session->gpsdata.dop.pdop,
269                     session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
270                     session->gpsdata.dop.tdop, session->newdata.mode,
271                     session->gpsdata.status);
272         return mask;
273
274     case 0x06:                  /* Channel Status Output */
275         session->context->gps_week = (unsigned short)getleuw(buf2, 2);
276         session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
277         /*@ ignore @*//*@ splint is confused @ */
278         session->gpsdata.skyview_time =
279             gpstime_to_unix(session->context->gps_week,
280                             session->context->gps_tow) -
281             session->context->leap_seconds;
282         /*@ end @*/
283         session->gpsdata.satellites_visible = (int)getub(buf2, 8);
284         gpsd_zero_satellites(&session->gpsdata);
285         memset(session->gpsdata.used, 0, sizeof(session->gpsdata.used));
286         if (session->gpsdata.satellites_visible > 12) {
287             gpsd_report(LOG_WARN,
288                         "Warning: EverMore packet has information about %d satellites!\n",
289                         session->gpsdata.satellites_visible);
290         }
291         if (session->gpsdata.satellites_visible > EVERMORE_CHANNELS)
292             session->gpsdata.satellites_visible = EVERMORE_CHANNELS;
293         satcnt = 0;
294         for (i = 0; i < (size_t) session->gpsdata.satellites_visible; i++) {
295             int prn;
296             // channel = getub(buf2, 7*i+7+2)
297             prn = (int)getub(buf2, 7 * i + 7 + 3);
298             if (prn == 0)
299                 continue;       /* satellite record is not valid */
300             session->gpsdata.PRN[satcnt] = prn;
301             session->gpsdata.azimuth[satcnt] =
302                 (int)getleuw(buf2, 7 * i + 7 + 4);
303             session->gpsdata.elevation[satcnt] =
304                 (int)getub(buf2, 7 * i + 7 + 6);
305             session->gpsdata.ss[satcnt] = (float)getub(buf2, 7 * i + 7 + 7);
306             /*
307              * Status bits at offset 8:
308              * bit0 = 1 satellite acquired
309              * bit1 = 1 code-tracking loop locked
310              * bit2 = 1 carrier-tracking loop locked
311              * bit3 = 1 data-bit synchronization done
312              * bit4 = 1 frame synchronization done
313              * bit5 = 1 ephemeris data collected
314              * bit6 = 1 used for position fix
315              */
316             if (getub(buf2, 7 * i + 7 + 8) & 0x40) {
317                 session->gpsdata.used[session->gpsdata.satellites_used++] =
318                     prn;
319             }
320
321             satcnt++;
322         }
323         session->gpsdata.satellites_visible = (int)satcnt;
324         /* that's all the information in this packet */
325         mask = SATELLITE_IS | USED_IS;
326         gpsd_report(LOG_DATA,
327                     "CSO 0x06: time=%.2f used=%d visible=%d mask={TIME|SATELLITE|USED}\n",
328                     session->newdata.time, session->gpsdata.satellites_used,
329                     session->gpsdata.satellites_visible);
330         return mask;
331
332     case 0x08:                  /* Measurement Data Output */
333         /* clock offset is a manufacturer diagnostic */
334         /* (int)getleuw(buf2, 8);  clock offset, 29000..29850 ?? */
335         session->context->gps_week = (unsigned short)getleuw(buf2, 2);
336         session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
337         /*@ ignore @*//*@ splint is confused @ */
338         session->newdata.time =
339             gpstime_to_unix(session->context->gps_week,
340                             session->context->gps_tow) -
341             session->context->leap_seconds;
342         /*@ end @*/
343         visible = (unsigned char)getub(buf2, 10);
344         /*
345          * Note: This code is untested. It was written from the manual.
346          * The results need to be sanity-checked against a GPS with
347          * known-good raw decoding and the same skyview.
348          *
349          * We can get pseudo range (m), delta-range (m/s), doppler (Hz)
350          * and status for each channel from the chip.  We cannot get
351          * codephase or carrierphase.
352          */
353 #define SBITS(sat, s, l)        sbits((char *)buf, 10 + (sat*14) + s, l)
354 #define UBITS(sat, s, l)        ubits((char *)buf, 10 + (sat*14) + s, l)
355         for (k = 0; k < visible; k++) {
356             int prn = (int)UBITS(k, 4, 5);
357             /* this is so we can tell which never got set */
358             for (j = 0; j < MAXCHANNELS; j++)
359                 session->gpsdata.raw.mtime[j] = 0;
360             for (j = 0; j < MAXCHANNELS; j++) {
361                 if (session->gpsdata.PRN[j] == prn) {
362                     session->gpsdata.raw.codephase[j] = NAN;
363                     session->gpsdata.raw.carrierphase[j] = NAN;
364                     session->gpsdata.raw.mtime[j] = session->newdata.time;
365                     session->gpsdata.raw.satstat[j] = (unsigned)UBITS(k, 24, 8);
366                     session->gpsdata.raw.pseudorange[j] = (double)SBITS(k,40,32);
367                     session->gpsdata.raw.deltarange[j] = (double)SBITS(k,72,32);
368                     session->gpsdata.raw.doppler[j] = (double)SBITS(k, 104, 16);
369                 }
370             }
371         }
372 #undef SBITS
373 #undef UBITS
374         gpsd_report(LOG_DATA, "MDO 0x04: time=%.2f mask={TIME|RAW}\n",
375                     session->newdata.time);
376         return TIME_IS | RAW_IS;
377
378     case 0x20:                  /* LogConfig Info, could be used as a probe for EverMore GPS */
379         gpsd_report(LOG_IO, "LogConfig EverMore packet, length %zd: %s\n",
380                     datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_IO));
381         return ONLINE_IS;
382
383     case 0x22:                  /* LogData */
384         gpsd_report(LOG_IO, "LogData EverMore packet, length %zd: %s\n",
385                     datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_IO));
386         return ONLINE_IS;
387
388     case 0x38:                  /* ACK */
389         gpsd_report(LOG_PROG, "EverMore command %02X ACK\n", getub(buf2, 2));
390         return ONLINE_IS;
391
392     default:
393         gpsd_report(LOG_WARN,
394                     "unknown EverMore packet EID 0x%02x, length %zd: %s\n",
395                     buf2[0], datalen, gpsd_hexdump_wrapper(buf2, datalen,
396                                                            LOG_IO));
397         return 0;
398     }
399 }
400
401 /*@ -charint @*/
402
403 static gps_mask_t evermore_parse_input(struct gps_device_t *session)
404 {
405     gps_mask_t st;
406
407     if (session->packet.type == EVERMORE_PACKET) {
408         st = evermore_parse(session, session->packet.outbuffer,
409                             session->packet.outbuflen);
410         return st;
411 #ifdef NMEA_ENABLE
412     } else if (session->packet.type == NMEA_PACKET) {
413         st = nmea_parse((char *)session->packet.outbuffer, session);
414         return st;
415 #endif /* NMEA_ENABLE */
416     } else
417         return 0;
418 }
419
420 /*@ +charint -usedef -compdef @*/
421 static ssize_t evermore_control_send(struct gps_device_t *session, char *buf,
422                                      size_t len)
423 {
424     unsigned int crc;
425     size_t i;
426     char *cp;
427
428     /*@ +charint +ignoresigns @*/
429     /* prepare a DLE-stuffed copy of the message */
430     cp = session->msgbuf;
431     *cp++ = 0x10;               /* message starts with DLE STX */
432     *cp++ = 0x02;
433
434     session->msgbuflen = (size_t) (len + 2);    /* len < 254 !! */
435     *cp++ = (char)session->msgbuflen;   /* message length */
436     if (session->msgbuflen == 0x10)
437         *cp++ = 0x10;
438
439     /* payload */
440     crc = 0;
441     for (i = 0; i < len; i++) {
442         *cp++ = buf[i];
443         if (buf[i] == 0x10)
444             *cp++ = 0x10;
445         crc += buf[i];
446     }
447
448     crc &= 0xff;
449
450     /* enter CRC after payload */
451     *cp++ = crc;
452     if (crc == 0x10)
453         *cp++ = 0x10;
454
455     *cp++ = 0x10;               /* message ends with DLE ETX */
456     *cp++ = 0x03;
457
458     session->msgbuflen = (size_t) (cp - session->msgbuf);
459     /*@ -charint -ignoresigns @*/
460
461     return gpsd_write(session, session->msgbuf, session->msgbuflen);
462 }
463
464 /*@ -charint +usedef +compdef @*/
465
466 static bool evermore_protocol(struct gps_device_t *session, int protocol)
467 {
468     /*@ +charint */
469     char tmp8;
470     char evrm_protocol_config[] = {
471         (char)0x84,             /* 0: msg ID, Protocol Configuration */
472         (char)0x00,             /* 1: mode; EverMore binary(0), NMEA(1) */
473         (char)0x00,             /* 2: reserved */
474         (char)0x00,             /* 3: reserved */
475     };
476     /*@ -charint */
477     gpsd_report(LOG_PROG, "evermore_protocol(%d)\n", protocol);
478     /*@i1@*/ tmp8 = (protocol != 0) ? 1 : 0;
479     /* NMEA : binary */
480     evrm_protocol_config[1] = tmp8;
481     return (evermore_control_send
482             (session, evrm_protocol_config,
483              sizeof(evrm_protocol_config)) != -1);
484 }
485
486 static bool evermore_nmea_config(struct gps_device_t *session, int mode)
487 /* mode = 0 : EverMore default */
488 /* mode = 1 : gpsd best */
489 /* mode = 2 : EverMore search, activate PEMT101 message */
490 {
491     unsigned char tmp8;
492     /*@ +charint */
493     unsigned char evrm_nmeaout_config[] = {
494         0x8e,                   /*  0: msg ID, NMEA Message Control */
495         0xff,                   /*  1: NMEA sentence bitmask, GGA(0), GLL(1), GSA(2), GSV(3), ... */
496         0x01,                   /*  2: nmea checksum no(0), yes(1) */
497         1,                      /*  3: GPGGA, interval 0-255s */
498         0,                      /*  4: GPGLL, interval 0-255s */
499         1,                      /*  5: GPGSA, interval 0-255s */
500         1,                      /*  6: GPGSV, interval 0-255s */
501         1,                      /*  7: GPRMC, interval 0-255s */
502         0,                      /*  8: GPVTG, interval 0-255s */
503         0,                      /*  9: PEMT,101, interval 0-255s */
504         0, 0, 0, 0, 0, 0,       /* 10-15: reserved */
505     };
506     /*@ -charint */
507     gpsd_report(LOG_PROG, "evermore_nmea_config(%d)\n", mode);
508     /*@i1@*/ tmp8 = (mode == 1) ? 5 : 1;
509     /* NMEA GPGSV, gpsd  */
510     evrm_nmeaout_config[6] = tmp8;      /* GPGSV, 1s or 5s */
511     /*@i1@*/ tmp8 = (mode == 2) ? 1 : 0;
512     /* NMEA PEMT101 */
513     evrm_nmeaout_config[9] = tmp8;      /* PEMT101, 1s or 0s */
514     return (evermore_control_send(session, (char *)evrm_nmeaout_config,
515                                   sizeof(evrm_nmeaout_config)) != -1);
516 }
517
518 static void evermore_mode(struct gps_device_t *session, int mode)
519 {
520     gpsd_report(LOG_PROG, "evermore_mode(%d), %d\n", mode,
521                 session->back_to_nmea ? 1 : 0);
522     if (mode == MODE_NMEA) {
523         /* NMEA */
524         (void)evermore_protocol(session, 1);
525         session->gpsdata.dev.driver_mode = MODE_NMEA;
526         (void)evermore_nmea_config(session, 1); /* configure NMEA messages for gpsd */
527     } else {
528         /* binary */
529         (void)evermore_protocol(session, 0);
530         session->back_to_nmea = false;
531         session->gpsdata.dev.driver_mode = MODE_BINARY;
532     }
533 }
534
535 static void evermore_event_hook(struct gps_device_t *session, event_t event)
536 {
537     /*
538      * FIX-ME: It might not be necessary to call this on reactivate.
539      * Experiment to see if the holds its settings through a close.
540      */
541     if (event == event_identified || event == event_reactivate) {
542         if (session->packet.type == NMEA_PACKET) {
543             (void)evermore_nmea_config(session, 1);     /* configure NMEA messages for gpsd (GPGSV every 5s) */
544         }
545         (void)evermore_mode(session, 1);        /* switch GPS to binary mode */
546         session->back_to_nmea = true;
547     } else if (event == event_deactivate) {
548         (void)evermore_nmea_config(session, 0); /* configure NMEA messages to default */
549     }
550 }
551
552 #ifdef ALLOW_RECONFIGURE
553 static bool evermore_speed(struct gps_device_t *session,
554                            speed_t speed, char parity, int stopbits)
555 {
556     /*@ -type @*/
557     gpsd_report(LOG_PROG, "evermore_speed(%u%c%d)\n", speed, parity,
558                 stopbits);
559     /* parity and stopbit switching aren't available on this chip */
560     if (parity != session->gpsdata.dev.parity
561         || stopbits != (int)session->gpsdata.dev.parity) {
562         return false;
563     } else {
564         unsigned char tmp8;
565         unsigned char msg[] = {
566             0x89,               /*  0: msg ID, Serial Port Configuration */
567             0x01,               /*  1: bit 0 cfg for main serial, bit 1 cfg for DGPS port */
568             0x00,               /*  2: baud rate for main serial; 4800(0), 9600(1), 19200(2), 38400(3) */
569             0x00,               /*  3: baud rate for DGPS serial port; 4800(0), 9600(1), etc */
570         };
571         switch (speed) {
572         case 4800:
573             tmp8 = 0;
574             break;
575         case 9600:
576             tmp8 = 1;
577             break;
578         case 19200:
579             tmp8 = 2;
580             break;
581         case 38400:
582             tmp8 = 3;
583             break;
584         default:
585             return false;
586         }
587         msg[2] = tmp8;
588         return (evermore_control_send(session, (char *)msg, sizeof(msg)) !=
589                 -1);
590     }
591     /*@ +type @*/
592 }
593
594 static bool evermore_rate_switcher(struct gps_device_t *session, double rate)
595 /* change the sample rate of the GPS */
596 {
597     /*@ +charint @*/
598     unsigned char evrm_rate_config[] = {
599         0x84,                   /* 1: msg ID, Operating Mode Configuration */
600         0x02,                   /* 2: normal mode with 1PPS */
601         0x00,                   /* 3: navigation update rate */
602         0x00,                   /* 4: RF/GPSBBP On Time */
603     };
604
605     if (rate < 1 || rate > 10) {
606         gpsd_report(LOG_ERROR, "valid rate range is 1-10.\n");
607         return false;
608     } else {
609         evrm_rate_config[2] = (unsigned char)trunc(rate);
610         return (evermore_control_send(session, (char *)evrm_rate_config,
611                                       sizeof(evrm_rate_config)) != -1);
612     }
613     /*@ -charint @*/
614 }
615 #endif /* ALLOW_RECONFIGURE */
616
617
618 /* this is everything we export */
619 /* *INDENT-OFF* */
620 const struct gps_type_t evermore_binary =
621 {
622     .type_name      = "EverMore binary",        /* full name of type */
623     .packet_type    = EVERMORE_PACKET,          /* lexer packet type */
624     .trigger        = "$PEMT,",                 /* recognize the type */
625     .channels       = EVERMORE_CHANNELS,        /* consumer-grade GPS */
626     .probe_detect   = NULL,                     /* no probe */
627     .get_packet     = generic_get,              /* use generic one */
628     .parse_packet   = evermore_parse_input,     /* parse message packets */
629     .rtcm_writer    = pass_rtcm,                /* send RTCM data straight */
630     .event_hook     = evermore_event_hook,      /* lifetime event handler */
631 #ifdef ALLOW_RECONFIGURE
632     .speed_switcher = evermore_speed,           /* we can change baud rates */
633     .mode_switcher  = evermore_mode,            /* there is a mode switcher */
634     .rate_switcher  = evermore_rate_switcher,   /* change sample rate */
635     .min_cycle      = 1,                        /* ignore, no rate switch */
636 #endif /* ALLOW_RECONFIGURE */
637 #ifdef ALLOW_CONTROLSEND
638     .control_send   = evermore_control_send,    /* how to send a control string */
639 #endif /* ALLOW_CONTROLSEND */
640 #ifdef NTPSHM_ENABLE
641     .ntp_offset     = NULL,             /* no method for NTP fudge factor */
642 #endif /* NTPSHM_ ENABLE */
643 };
644 /* *INDENT-ON* */
645 #endif /* defined(EVERMORE_ENABLE) && defined(BINARY_ENABLE) */