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.
7 * The vendor site is <http://www.emt.com.tw>.
9 * This driver was written by Petr Slansky based on a framework by Eric S.
10 * Raymond. The following remarks are by Petr Slansky.
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):
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)
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
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)
27 * These don't entail a reset of GPS as the 0x80 message does.
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
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:
38 * $PEMT,100,05.42g,100303,180,05,1,20,15,08,0,0,2,1*5A
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
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)
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
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)
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)
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.
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
94 * This is an exampe of an 0x8e message that activates all NMEA sentences
96 * 10 02 12 8E 7F 01 01 01 01 01 01 01 01 00 00 00 00 00 00 15 10 03
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
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
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
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.
117 #include <sys/types.h>
125 #endif /* S_SPLINT_S */
130 #if defined(EVERMORE_ENABLE) && defined(BINARY_ENABLE)
136 #define EVERMORE_CHANNELS 12
139 gps_mask_t evermore_parse(struct gps_device_t * session, unsigned char *buf,
142 unsigned char buf2[MAX_PACKET_LENGTH], *cp, *tp;
144 unsigned int type, used, visible, satcnt, j, k;
151 /* time to unstuff it and discard the header and footer */
156 datalen = (size_t) * cp++;
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));
164 for (i = 0; i < (size_t) datalen; i++) {
171 type = (unsigned char)getub(buf2, 1);
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 @*/
179 (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
182 session->cycle_end_reliable = true;
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;
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 */
203 session->newdata.mode = MODE_NO_FIX;
205 session->newdata.mode = MODE_2D;
207 session->newdata.mode = MODE_3D;
208 mask |= ALTITUDE_IS | CLIMB_IS;
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),
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;
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;
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.
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;
251 case 2: /* 2D navigation */
252 session->gpsdata.status = STATUS_FIX;
253 session->newdata.mode = MODE_2D;
255 case 3: /* 3D navigation */
256 session->gpsdata.status = STATUS_FIX;
257 session->newdata.mode = MODE_3D;
259 case 4: /* 3D navigation with DGPS */
260 session->gpsdata.status = STATUS_DGPS_FIX;
261 session->newdata.mode = MODE_3D;
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);
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;
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);
291 if (session->gpsdata.satellites_visible > EVERMORE_CHANNELS)
292 session->gpsdata.satellites_visible = EVERMORE_CHANNELS;
294 for (i = 0; i < (size_t) session->gpsdata.satellites_visible; i++) {
296 // channel = getub(buf2, 7*i+7+2)
297 prn = (int)getub(buf2, 7 * i + 7 + 3);
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);
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
316 if (getub(buf2, 7 * i + 7 + 8) & 0x40) {
317 session->gpsdata.used[session->gpsdata.satellites_used++] =
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);
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;
343 visible = (unsigned char)getub(buf2, 10);
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.
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.
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);
374 gpsd_report(LOG_DATA, "MDO 0x04: time=%.2f mask={TIME|RAW}\n",
375 session->newdata.time);
376 return TIME_IS | RAW_IS;
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));
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));
389 gpsd_report(LOG_PROG, "EverMore command %02X ACK\n", getub(buf2, 2));
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,
403 static gps_mask_t evermore_parse_input(struct gps_device_t *session)
407 if (session->packet.type == EVERMORE_PACKET) {
408 st = evermore_parse(session, session->packet.outbuffer,
409 session->packet.outbuflen);
412 } else if (session->packet.type == NMEA_PACKET) {
413 st = nmea_parse((char *)session->packet.outbuffer, session);
415 #endif /* NMEA_ENABLE */
420 /*@ +charint -usedef -compdef @*/
421 static ssize_t evermore_control_send(struct gps_device_t *session, char *buf,
428 /*@ +charint +ignoresigns @*/
429 /* prepare a DLE-stuffed copy of the message */
430 cp = session->msgbuf;
431 *cp++ = 0x10; /* message starts with DLE STX */
434 session->msgbuflen = (size_t) (len + 2); /* len < 254 !! */
435 *cp++ = (char)session->msgbuflen; /* message length */
436 if (session->msgbuflen == 0x10)
441 for (i = 0; i < len; i++) {
450 /* enter CRC after payload */
455 *cp++ = 0x10; /* message ends with DLE ETX */
458 session->msgbuflen = (size_t) (cp - session->msgbuf);
459 /*@ -charint -ignoresigns @*/
461 return gpsd_write(session, session->msgbuf, session->msgbuflen);
464 /*@ -charint +usedef +compdef @*/
466 static bool evermore_protocol(struct gps_device_t *session, int protocol)
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 */
477 gpsd_report(LOG_PROG, "evermore_protocol(%d)\n", protocol);
478 /*@i1@*/ tmp8 = (protocol != 0) ? 1 : 0;
480 evrm_protocol_config[1] = tmp8;
481 return (evermore_control_send
482 (session, evrm_protocol_config,
483 sizeof(evrm_protocol_config)) != -1);
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 */
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 */
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;
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);
518 static void evermore_mode(struct gps_device_t *session, int mode)
520 gpsd_report(LOG_PROG, "evermore_mode(%d), %d\n", mode,
521 session->back_to_nmea ? 1 : 0);
522 if (mode == MODE_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 */
529 (void)evermore_protocol(session, 0);
530 session->back_to_nmea = false;
531 session->gpsdata.dev.driver_mode = MODE_BINARY;
535 static void evermore_event_hook(struct gps_device_t *session, event_t event)
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.
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) */
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 */
552 #ifdef ALLOW_RECONFIGURE
553 static bool evermore_speed(struct gps_device_t *session,
554 speed_t speed, char parity, int stopbits)
557 gpsd_report(LOG_PROG, "evermore_speed(%u%c%d)\n", speed, parity,
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) {
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 */
588 return (evermore_control_send(session, (char *)msg, sizeof(msg)) !=
594 static bool evermore_rate_switcher(struct gps_device_t *session, double rate)
595 /* change the sample rate of the GPS */
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 */
605 if (rate < 1 || rate > 10) {
606 gpsd_report(LOG_ERROR, "valid rate range is 1-10.\n");
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);
615 #endif /* ALLOW_RECONFIGURE */
618 /* this is everything we export */
620 const struct gps_type_t evermore_binary =
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 */
641 .ntp_offset = NULL, /* no method for NTP fudge factor */
642 #endif /* NTPSHM_ ENABLE */
645 #endif /* defined(EVERMORE_ENABLE) && defined(BINARY_ENABLE) */