1 /*
2  * This file is Copyright (c) 2010-2018 by the GPSD project
3  * SPDX-License-Identifier: BSD-2-clause
4  */
5 
6 #include "gpsd_config.h"  /* must be before all includes */
7 
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include "gpsd.h"
11 
12 #if defined(ONCORE_ENABLE) && defined(BINARY_ENABLE)
13 #include "bits.h"
14 #include "timespec.h"
15 
16 static char enableEa[] = { 'E', 'a', 1 };
17 static char enableBb[] = { 'B', 'b', 1 };
18 static char getfirmware[] = { 'C', 'j' };
19 /*static char enableEn[] =
20     { 'E', 'n', 1, 0, 100, 100, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };*/
21 /*static char enableAt2[] 	= { 'A', 't', 2, };*/
22 static unsigned char pollAs[] =
23     { 'A', 's', 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff, 0xff, 0xff, 0x7f, 0xff,
24     0xff, 0xff, 0xff
25 };
26 static unsigned char pollAt[] = { 'A', 't', 0xff };
27 static unsigned char pollAy[] = { 'A', 'y', 0xff, 0xff, 0xff, 0xff };
28 static unsigned char pollBo[] = { 'B', 'o', 0x01 };
29 static unsigned char pollEn[] = {
30     'E', 'n', 0xff, 0xff, 0xff, 0xff, 0xff,
31     0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
32 };
33 
34 
35 /*
36  * These routines are specific to this driver
37  */
38 
39 static gps_mask_t oncore_parse_input(struct gps_device_t *);
40 static gps_mask_t oncore_dispatch(struct gps_device_t *, unsigned char *,
41 				  size_t);
42 static gps_mask_t oncore_msg_navsol(struct gps_device_t *, unsigned char *,
43 				    size_t);
44 static gps_mask_t oncore_msg_utc_offset(struct gps_device_t *,
45 					unsigned char *, size_t);
46 static gps_mask_t oncore_msg_pps_offset(struct gps_device_t *, unsigned char *,
47 					size_t);
48 static gps_mask_t oncore_msg_svinfo(struct gps_device_t *, unsigned char *,
49 				    size_t);
50 static gps_mask_t oncore_msg_time_raim(struct gps_device_t *, unsigned char *,
51 				       size_t);
52 static gps_mask_t oncore_msg_firmware(struct gps_device_t *, unsigned char *,
53 				      size_t);
54 
55 /*
56  * These methods may be called elsewhere in gpsd
57  */
58 static ssize_t oncore_control_send(struct gps_device_t *, char *, size_t);
59 static void oncore_event_hook(struct gps_device_t *, event_t);
60 
61 /*
62  * Decode the navigation solution message
63  *
64  * @@Ea - Position/Status/Data Message
65 
66 @@EamdyyhmsffffaaaaoooohhhhmmmmvvhhddtntimsdimsdimsdimsdimsdimsdimsdimsdsC
67 <CR><LF>
68 
69  *
70  *      Date
71  *       m         - month                       1 .. 12
72  *       d         - day                         1 .. 31
73  *       yy        - year                        1980 .. 2079
74  *
75  *      Time
76  *       h         - hours                       0 .. 23
77  *       m         - minutes                     0 .. 59
78  *       s         - seconds                     0 .. 60
79  *       ffff      - fractional second           0 .. 999,999,999
80  *                 (0.0 .. 0.999999999)
81  *
82  *      Position
83  *       aaaa      - latitude in mas             -324,000,000 .. 324,000,000
84  *                 (-90 degrees .. 90 degrees)
85  *
86  *       oooo      - longitude in mas            -648,000,000 .. 648,000,000
87  *                 (-180 degrees .. 180 degrees)
88  *
89  *       hhhh       - ellipsoid height in cm     -100,000 .. 1,800,000
90  *                 (-1000.00 .. 18,000.00m)*
91  *
92  *       mmmm  - not used                        0
93  *
94  *      Velocity
95  *       vv        - velocity in cm/s            0 .. 51,400 (0..514.00 m/s)*
96  *       hh        - heading                     0 .. 3599 (0.0..359.9 degrees)
97  *
98  *                 (true north res 0.1 degrees)
99  *
100  *      Geometry
101  *       dd        - current DOP (0.1 res)       0 .. 999 (0.0 to 99.9 DOP)
102  *             (0 = not computable, position-hold, or    position propagate)
103  *
104  *       t         - DOP TYPE
105  *                   0   PDOP (3D)/antenna ok
106  *                   1   PDOP (3D)/antenna OK
107  *                   64  PDOP (3D)/antenna shorted
108  *                   65  PDOP (3D)/antenna shorted
109  *                   128 PDOP (3D)/antenna open
110  *                   129 PDOP (3D)/antenna open
111  *                   192 PDOP (3D)/antenna shorted
112  *                   193 PDOP (3D)/antenna shorted
113  *
114  *      Satellite visibility and tracking status
115  *       n         - num of visible sats         0 .. 12
116  *       t         - num of satellites tracked   0 .. 8
117  *
118  *      For each of eight receiver channels
119  *       i         - sat ID                      0 .. 37
120  *       m         - channel tracking mode       0 .. 8
121  *                 0 = code search    5 = message sync detect
122  *                 1 = code acquire   6 = satellite time avail.
123  *                 2 = AGC set        7 = ephemeris acquire
124  *                 3 = prep acquire   8 = avail for position
125  *                 4 = bit sync detect
126  *
127  *       s         - carrier to noise density ratio
128  *                 (C/No)                        0 .. 255 db-Hz
129  *
130  *       d         - channel status flag
131  *                 Each bit represents one of the following:
132  *                 (msb)   Bit 7: using for position fix
133  *                         Bit 6: satellite momentum alert flag
134  *                         Bit 5: satellite anti-spoof flag set
135  *                         Bit 4: satellite reported unhealthy
136  *                         Bit 3: satellite reported inaccurate
137  *                         (> 16m)
138  *                         Bit 2: spare
139  *                         Bit 1: spare
140  *                 (lsb)   Bit 0: parity error
141  *
142  *      End of channel dependent data
143  *       s         - receiver status flag
144  *
145  *                 Each bit represents one of the following:
146  *                 (msb)   Bit 7: position propagate mode
147  *                         Bit 6: poor geometry (DOP > 12)
148  *                         Bit 5: 3D fix
149  *                         Bit 4: 2D fix
150  *                         Bit 3: acquiring satellites/position hold
151  *                         Bit 2: spare
152  *                         Bit 1: insufficient visible satellites
153  *                         (< 3)
154  *                 (lsb)   Bit 0: bad almanac
155  *
156  *       C  - checksum
157  *       Message length: 76 bytes
158  *
159  */
160 static gps_mask_t
oncore_msg_navsol(struct gps_device_t * session,unsigned char * buf,size_t data_len)161 oncore_msg_navsol(struct gps_device_t *session, unsigned char *buf,
162 		  size_t data_len)
163 {
164     gps_mask_t mask;
165     unsigned char flags;
166     double lat, lon, alt;
167     float speed, track, dop;
168     unsigned int i, j, st, nsv;
169     int Bbused;
170     struct tm unpacked_date;
171     char ts_buf[TIMESPEC_LEN];
172 
173     if (data_len != 76)
174 	return 0;
175 
176     mask = ONLINE_SET;
177     GPSD_LOG(LOG_DATA, &session->context->errout,
178 	     "oncore NAVSOL - navigation data\n");
179 
180     flags = (unsigned char)getub(buf, 72);
181 
182     if (flags & 0x20) {
183 	session->gpsdata.status = STATUS_FIX;
184 	session->newdata.mode = MODE_3D;
185     } else if (flags & 0x10) {
186 	session->gpsdata.status = STATUS_FIX;
187 	session->newdata.mode = MODE_2D;
188     } else {
189 	GPSD_LOG(LOG_WARN, &session->context->errout,
190 		 "oncore NAVSOL no fix - flags 0x%02x\n", flags);
191 	session->newdata.mode = MODE_NO_FIX;
192 	session->gpsdata.status = STATUS_NO_FIX;
193     }
194     mask |= MODE_SET;
195 
196     /* Unless we have seen non-zero utc offset data, the time is GPS time
197      * and not UTC time.  Do not use it.
198      */
199     if (session->context->leap_seconds) {
200 	unsigned int nsec;
201 	unpacked_date.tm_mon = (int)getub(buf, 4) - 1;
202 	unpacked_date.tm_mday = (int)getub(buf, 5);
203 	unpacked_date.tm_year = (int)getbeu16(buf, 6) - 1900;
204 	unpacked_date.tm_hour = (int)getub(buf, 8);
205 	unpacked_date.tm_min = (int)getub(buf, 9);
206 	unpacked_date.tm_sec = (int)getub(buf, 10);
207 	unpacked_date.tm_isdst = 0;
208 	unpacked_date.tm_wday = unpacked_date.tm_yday = 0;
209 	nsec = (unsigned int) getbeu32(buf, 11);
210 
211 	session->newdata.time.tv_sec = mkgmtime(&unpacked_date);
212 	session->newdata.time.tv_nsec = nsec;
213 	mask |= TIME_SET;
214 	GPSD_LOG(LOG_DATA, &session->context->errout,
215 		 "oncore NAVSOL - time: %04d-%02d-%02d %02d:%02d:%02d.%09d\n",
216 		 unpacked_date.tm_year + 1900, unpacked_date.tm_mon + 1,
217 		 unpacked_date.tm_mday, unpacked_date.tm_hour,
218 		 unpacked_date.tm_min, unpacked_date.tm_sec, nsec);
219     }
220 
221     lat = getbes32(buf, 15) / 3600000.0f;
222     lon = getbes32(buf, 19) / 3600000.0f;
223     alt = getbes32(buf, 23) / 100.0f;
224     speed = getbeu16(buf, 31) / 100.0f;
225     track = getbeu16(buf, 33) / 10.0f;
226     dop = getbeu16(buf, 35) / 10.0f;
227 
228     GPSD_LOG(LOG_DATA, &session->context->errout,
229 	     "oncore NAVSOL - %lf %lf %.2lfm | %.2fm/s %.1fdeg dop=%.1f\n",
230 	     lat, lon, alt, speed, track,
231 	     (float)dop);
232 
233     session->newdata.latitude = lat;
234     session->newdata.longitude = lon;
235     session->newdata.altHAE = alt;  /* is WGS84 */
236     session->newdata.speed = speed;
237     session->newdata.track = track;
238 
239     mask |= LATLON_SET | ALTITUDE_SET | SPEED_SET | TRACK_SET;
240 
241     gpsd_zero_satellites(&session->gpsdata);
242     /* Merge the satellite information from the Bb message. */
243     Bbused = 0;
244     nsv = 0;
245     for (i = st = 0; i < 8; i++) {
246 	int sv, mode, sn, status;
247 	unsigned int off;
248 
249 	off = 40 + 4 * i;
250 	sv = (int)getub(buf, off);
251 	mode = (int)getub(buf, off + 1);
252 	sn = (int)getub(buf, off + 2);
253 	status = (int)getub(buf, off + 3);
254 
255 	GPSD_LOG(LOG_DATA, &session->context->errout,
256 		 "%2d %2d %2d %3d %02x\n", i, sv, mode, sn, status);
257 
258 	if (sn) {
259 	    session->gpsdata.skyview[st].PRN = (short)sv;
260 	    session->gpsdata.skyview[st].ss = (double)sn;
261 	    for (j = 0; (int)j < session->driver.oncore.visible; j++)
262 		if (session->driver.oncore.PRN[j] == sv) {
263 		    session->gpsdata.skyview[st].elevation =
264 			(double)session->driver.oncore.elevation[j];
265 		    session->gpsdata.skyview[st].azimuth =
266 			(double)session->driver.oncore.azimuth[j];
267 		    Bbused |= 1 << j;
268 		    break;
269 		}
270 	    /* bit 7 of the status word: sat used for position */
271 	    session->gpsdata.skyview[st].used = false;
272 	    if (status & 0x80) {
273 		session->gpsdata.skyview[st].used = true;
274 		nsv++;
275 	    }
276 	    /* bit 2 of the status word: using for time solution */
277 	    if (status & 0x02)
278 		mask |= NTPTIME_IS | GOODTIME_IS;
279 	    /*
280 	     * The GOODTIME_IS mask bit exists distinctly from TIME_SET exactly
281 	     * so an OnCore running in time-service mode (and other GPS clocks)
282 	     * can signal that it's returning time even though no position fixes
283 	     * have been available.
284 	     */
285 	    st++;
286 	}
287     }
288     for (j = 0; (int)j < session->driver.oncore.visible; j++)
289 	if (!(Bbused & (1 << j))) {
290 	    session->gpsdata.skyview[st].PRN =
291                 (short)session->driver.oncore.PRN[j];
292 	    session->gpsdata.skyview[st].elevation =
293 		(double)session->driver.oncore.elevation[j];
294 	    session->gpsdata.skyview[st].azimuth =
295 		(double)session->driver.oncore.azimuth[j];
296 	    st++;
297 	}
298     session->gpsdata.skyview_time = session->newdata.time;
299     session->gpsdata.satellites_used = (int)nsv;
300     session->gpsdata.satellites_visible = (int)st;
301 
302     mask |= SATELLITE_SET | USED_IS;
303 
304     /* Some messages can only be polled.  As they are not so
305      * important, would be enough to poll e.g. one message per cycle.
306      */
307     (void)oncore_control_send(session, (char *)pollAs, sizeof(pollAs));
308     (void)oncore_control_send(session, (char *)pollAt, sizeof(pollAt));
309     (void)oncore_control_send(session, (char *)pollAy, sizeof(pollAy));
310     (void)oncore_control_send(session, (char *)pollBo, sizeof(pollBo));
311     (void)oncore_control_send(session, (char *)pollEn, sizeof(pollEn));
312 
313     GPSD_LOG(LOG_DATA, &session->context->errout,
314 	     "NAVSOL: time=%s lat=%.2f lon=%.2f altMSL=%.2f speed=%.2f "
315              "track=%.2f mode=%d status=%d visible=%d used=%d\n",
316              timespec_str(&session->newdata.time, ts_buf, sizeof(ts_buf)),
317              session->newdata.latitude,
318 	     session->newdata.longitude, session->newdata.altHAE,
319 	     session->newdata.speed, session->newdata.track,
320 	     session->newdata.mode, session->gpsdata.status,
321 	     session->gpsdata.satellites_used,
322 	     session->gpsdata.satellites_visible);
323     return mask;
324 }
325 
326 /**
327  * GPS Leap Seconds = UTC offset
328  */
329 static gps_mask_t
oncore_msg_utc_offset(struct gps_device_t * session,unsigned char * buf,size_t data_len)330 oncore_msg_utc_offset(struct gps_device_t *session, unsigned char *buf,
331 		      size_t data_len)
332 {
333     int utc_offset;
334 
335     if (data_len != 8)
336 	return 0;
337 
338     GPSD_LOG(LOG_DATA, &session->context->errout,
339 	     "oncore UTCTIME - leap seconds\n");
340     utc_offset = (int)getub(buf, 4);
341     if (utc_offset == 0)
342 	return 0;		/* that part of almanac not received yet */
343 
344     session->context->leap_seconds = utc_offset;
345     session->context->valid |= LEAP_SECOND_VALID;
346     return 0;			/* no flag for leap seconds update */
347 }
348 
349 /**
350  * PPS offset
351  */
352 static gps_mask_t
oncore_msg_pps_offset(struct gps_device_t * session,unsigned char * buf,size_t data_len)353 oncore_msg_pps_offset(struct gps_device_t *session, unsigned char *buf,
354 		      size_t data_len)
355 {
356     int pps_offset_ns;
357 
358     if (data_len != 11)
359 	return 0;
360 
361     GPSD_LOG(LOG_DATA, &session->context->errout, "oncore PPS offset\n");
362     pps_offset_ns = (int)getbes32(buf, 4);
363 
364     session->driver.oncore.pps_offset_ns = pps_offset_ns;
365     return 0;
366 }
367 
368 /**
369  * GPS Satellite Info
370  */
371 static gps_mask_t
oncore_msg_svinfo(struct gps_device_t * session,unsigned char * buf,size_t data_len)372 oncore_msg_svinfo(struct gps_device_t *session, unsigned char *buf,
373 		  size_t data_len)
374 {
375     unsigned int i, nchan;
376     int j;
377 
378     if (data_len != 92)
379 	return 0;
380 
381     GPSD_LOG(LOG_DATA, &session->context->errout,
382 	     "oncore SVINFO - satellite data\n");
383     nchan = (unsigned int)getub(buf, 4);
384     GPSD_LOG(LOG_DATA, &session->context->errout,
385 	     "oncore SVINFO - %d satellites:\n", nchan);
386     /* Then we clamp the value to not read outside the table. */
387     if (nchan > 12)
388 	nchan = 12;
389     session->driver.oncore.visible = (int)nchan;
390     for (i = 0; i < nchan; i++) {
391 	/* get info for one channel/satellite */
392 	unsigned int off = 5 + 7 * i;
393 
394 	int sv = (int)getub(buf, off);
395 	int el = (int)getub(buf, off + 3);
396 	int az = (int)getbeu16(buf, off + 4);
397 
398 	GPSD_LOG(LOG_DATA, &session->context->errout,
399 		 "%2d %2d %2d %3d\n", i, sv, el, az);
400 
401 	/* Store for use when Ea messages come. */
402 	session->driver.oncore.PRN[i] = sv;
403 	session->driver.oncore.elevation[i] = (short)el;
404 	session->driver.oncore.azimuth[i] = (short)az;
405 	/* If it has an entry in the satellite list, update it! */
406 	for (j = 0; j < session->gpsdata.satellites_visible; j++)
407 	    if (session->gpsdata.skyview[j].PRN == (short)sv) {
408 		session->gpsdata.skyview[j].elevation = (double)el;
409 		session->gpsdata.skyview[j].azimuth = (double)az;
410 	    }
411     }
412 
413     GPSD_LOG(LOG_DATA, &session->context->errout,
414 	     "SVINFO: mask={SATELLITE}\n");
415     return SATELLITE_SET;
416 }
417 
418 /**
419  * GPS Time RAIM
420  */
421 static gps_mask_t
oncore_msg_time_raim(struct gps_device_t * session UNUSED,unsigned char * buf UNUSED,size_t data_len UNUSED)422 oncore_msg_time_raim(struct gps_device_t *session UNUSED,
423 		     unsigned char *buf UNUSED, size_t data_len UNUSED)
424 {
425     int sawtooth_ns;
426 
427     if (data_len != 69)
428 	return 0;
429 
430     sawtooth_ns = (int)getub(buf, 25);
431     GPSD_LOG(LOG_DATA, &session->context->errout,
432 	     "oncore PPS sawtooth: %d\n",sawtooth_ns);
433 
434     /* session->driver.oncore.traim_sawtooth_ns = sawtooth_ns; */
435 
436     return 0;
437 }
438 
439 /**
440  * GPS Firmware
441  */
442 static gps_mask_t
oncore_msg_firmware(struct gps_device_t * session UNUSED,unsigned char * buf UNUSED,size_t data_len UNUSED)443 oncore_msg_firmware(struct gps_device_t *session UNUSED,
444 		    unsigned char *buf UNUSED, size_t data_len UNUSED)
445 {
446     return 0;
447 }
448 
449 #define ONCTYPE(id2,id3) ((((unsigned int)id2)<<8)|(id3))
450 
451 /**
452  * Parse the data from the device
453  */
oncore_dispatch(struct gps_device_t * session,unsigned char * buf,size_t len)454 gps_mask_t oncore_dispatch(struct gps_device_t * session, unsigned char *buf,
455 			   size_t len)
456 {
457     unsigned int type;
458 
459     if (len == 0)
460 	return 0;
461 
462     type = ONCTYPE(buf[2], buf[3]);
463 
464     /* we may need to dump the raw packet */
465     GPSD_LOG(LOG_RAW, &session->context->errout,
466 	     "raw Oncore packet type 0x%04x\n", type);
467 
468     session->cycle_end_reliable = true;
469 
470     switch (type) {
471     case ONCTYPE('B', 'b'):
472 	return oncore_msg_svinfo(session, buf, len);
473     case ONCTYPE('E', 'a'):
474 	return oncore_msg_navsol(session, buf, len) | (CLEAR_IS | REPORT_IS);
475     case ONCTYPE('E', 'n'):
476 	return oncore_msg_time_raim(session, buf, len);
477     case ONCTYPE('C', 'j'):
478 	return oncore_msg_firmware(session, buf, len);
479     case ONCTYPE('B', 'o'):
480 	return oncore_msg_utc_offset(session, buf, len);
481     case ONCTYPE('A', 's'):
482 	return 0;		/* position hold mode */
483     case ONCTYPE('A', 't'):
484 	return 0;		/* position hold position */
485     case ONCTYPE('A', 'y'):
486 	return oncore_msg_pps_offset(session, buf, len);
487 
488     default:
489 	/* FIX-ME: This gets noisy in a hurry. Change once your driver works */
490 	GPSD_LOG(LOG_WARN, &session->context->errout,
491 		 "unknown packet id @@%c%c length %zd\n",
492 		 type >> 8, type & 0xff, len);
493 	return 0;
494     }
495 }
496 
497 
498 /**********************************************************
499  *
500  * Externally called routines below here
501  *
502  **********************************************************/
503 
504 /**
505  * Write data to the device, doing any required padding or checksumming
506  */
oncore_control_send(struct gps_device_t * session,char * msg,size_t msglen)507 static ssize_t oncore_control_send(struct gps_device_t *session,
508 				   char *msg, size_t msglen)
509 {
510     size_t i;
511     char checksum = 0;
512 
513     session->msgbuf[0] = '@';
514     session->msgbuf[1] = '@';
515     for (i = 0; i < msglen; i++) {
516 	checksum ^= session->msgbuf[i + 2] = msg[i];
517     }
518     session->msgbuf[msglen + 2] = checksum;
519     session->msgbuf[msglen + 3] = '\r';
520     session->msgbuf[msglen + 4] = '\n';
521     session->msgbuflen = msglen + 5;
522 
523     GPSD_LOG(LOG_PROG, &session->context->errout,
524 	     "writing oncore control type %c%c\n", msg[0], msg[1]);
525     return gpsd_write(session, session->msgbuf, session->msgbuflen);
526 }
527 
528 
oncore_event_hook(struct gps_device_t * session,event_t event)529 static void oncore_event_hook(struct gps_device_t *session, event_t event)
530 {
531     if (session->context->readonly)
532 	return;
533 
534     /*
535      * Some oncore VP variants that have not been used after long
536      * power-down will be silent on startup.  Provoke
537      * identification by requesting the firmware version.
538      */
539     if (event == event_wakeup)
540 	(void)oncore_control_send(session, getfirmware, sizeof(getfirmware));
541 
542     /*
543      * FIX-ME: It might not be necessary to call this on reactivate.
544      * Experiment to see if the holds its settings through a close.
545      */
546     if (event == event_identified || event == event_reactivate) {
547 	(void)oncore_control_send(session, enableEa, sizeof(enableEa));
548 	(void)oncore_control_send(session, enableBb, sizeof(enableBb));
549 	/*(void)oncore_control_send(session, enableEn, sizeof(enableEn)); */
550 	/*(void)oncore_control_send(session,enableAt2,sizeof(enableAt2)); */
551 	/*(void)oncore_control_send(session,pollAs,sizeof(pollAs)); */
552 	(void)oncore_control_send(session, (char*)pollBo, sizeof(pollBo));
553     }
554 }
555 
oncore_time_offset(struct gps_device_t * session UNUSED)556 static double oncore_time_offset(struct gps_device_t *session UNUSED)
557 {
558     /*
559      * Only one sentence (NAVSOL) ships time.  0.175 seems best at
560      * 9600 for UT+, not sure what the fudge should be at other baud
561      * rates or for other models.
562      */
563     return 0.175;
564 }
565 
oncore_parse_input(struct gps_device_t * session)566 static gps_mask_t oncore_parse_input(struct gps_device_t *session)
567 {
568     if (session->lexer.type == ONCORE_PACKET) {
569 	return oncore_dispatch(session, session->lexer.outbuffer,
570 			     session->lexer.outbuflen);
571 #ifdef NMEA0183_ENABLE
572     } else if (session->lexer.type == NMEA_PACKET) {
573 	return nmea_parse((char *)session->lexer.outbuffer, session);
574 #endif /* NMEA0183_ENABLE */
575     } else
576 	return 0;
577 }
578 
579 /* This is everything we export */
580 /* *INDENT-OFF* */
581 const struct gps_type_t driver_oncore = {
582 
583     .type_name        = "Motorola Oncore",	/* Full name of type */
584     .packet_type      = ONCORE_PACKET,		/* numeric packet type */
585     .flags	      = DRIVER_STICKY,		/* remember this */
586     .trigger          = NULL,			/* identifying response */
587     .channels         = 12,			/* device channel count */
588     .probe_detect     = NULL,			/* no probe */
589     .get_packet       = generic_get,		/* packet getter */
590     .parse_packet     = oncore_parse_input,	/* packet parser */
591     .rtcm_writer      = gpsd_write,		/* device accepts RTCM */
592     .init_query       = NULL,			/* non-perturbing query */
593     .event_hook       = oncore_event_hook,	/* lifetime event hook */
594 #ifdef RECONFIGURE_ENABLE
595     .speed_switcher   = NULL,			/* no speed setter */
596     .mode_switcher    = NULL,			/* no mode setter */
597     .rate_switcher    = NULL,			/* no speed setter */
598     .min_cycle.tv_sec  = 1,                     /* 1Hz */
599     .min_cycle.tv_nsec = 0,
600 #endif /* RECONFIGURE_ENABLE */
601 #ifdef CONTROLSEND_ENABLE
602     /* Control string sender - should provide checksum and headers/trailer */
603     .control_send   = oncore_control_send,	/* to send control strings */
604 #endif /* CONTROLSEND_ENABLE */
605     .time_offset = oncore_time_offset,		/* NTP offset array */
606 };
607 /* *INDENT-ON* */
608 #endif /* defined(ONCORE_ENABLE) && defined(BINARY_ENABLE) */
609