1 /*
2  * This is the gpsd driver for GeoStar Navigation receivers
3  * operating in binary mode.
4  *
5  * Tested with GeoS-1M GPS/GLONASS receiver.
6  *
7  * By Viktar Palstsiuk, viktar.palstsiuk@promwad.com
8  *
9  * This file is Copyright (c) 2010-2019 by the GPSD project
10  * SPDX-License-Identifier: BSD-2-clause
11  */
12 
13 #include "gpsd_config.h"  /* must be before all includes */
14 
15 #include <stdbool.h>
16 #include <stdio.h>
17 #include <math.h>
18 #include <string.h>
19 
20 #include "gpsd.h"
21 #include "bits.h"
22 #include "strfuncs.h"
23 #include "timespec.h"
24 
25 #ifdef GEOSTAR_ENABLE
26 #define GEOSTAR_CHANNELS	24
27 
28 #define JAN_2008		0x47798280 /* 1199145600 = 2008 - 1970 in seconds */
29 
30 #define OFFSET(n)		((n)*4+4)
31 
decode_channel_id(uint32_t ch_id)32 static int decode_channel_id (uint32_t ch_id) {
33 	int num = 0;
34 	num = (int)(ch_id & 0x1F);				/* SV ID */
35 	if((ch_id & (1<<30)) == 0) num += GLONASS_PRN_OFFSET;	/* GLONASS SV */
36 	else if (num == 0 ) num = 32;				/* GPS SV */
37 	return num;
38 }
39 
geostar_write(struct gps_device_t * session,unsigned int id,unsigned char * data,size_t len)40 static int geostar_write(struct gps_device_t *session,
41 			 unsigned int id, unsigned char *data, size_t len)
42 {
43     int i;
44     unsigned long cs = 0;
45 
46     putbyte(session->msgbuf, 0, 'P');
47     putbyte(session->msgbuf, 1, 'S');
48     putbyte(session->msgbuf, 2, 'G');
49     putbyte(session->msgbuf, 3, 'G');
50 
51     putbe16(session->msgbuf, 4, id);
52     putbe16(session->msgbuf, 6, len);
53 
54     /* Copy content */
55     memcpy(session->msgbuf + 8, data, len * 4);
56 
57     len += 2; /* PSGG + id + len */
58 
59     /* Calculate checksum */
60     for (i = 0; (size_t)i < len; i++) {
61 	cs ^= getleu32(session->msgbuf, i * 4);
62     }
63 
64     putle32(session->msgbuf, len * 4, cs);
65 
66     len += 1; /* Checksum */
67 
68     session->msgbuflen = len * 4;
69 
70     GPSD_LOG(LOG_PROG, &session->context->errout,
71 	     "Sent GeoStar packet id 0x%x\n", id);
72     if (gpsd_write(session, session->msgbuf, session->msgbuflen) !=
73 	(ssize_t) session->msgbuflen)
74 	return -1;
75 
76     return 0;
77 }
78 
79 /* geostar_detect()
80  *
81  * see if it looks like a GeoStar device is listening and
82  * return 1 if found, 0 if not
83  */
geostar_detect(struct gps_device_t * session)84 static bool geostar_detect(struct gps_device_t *session)
85 {
86     unsigned char buf[1 * 4];
87     bool ret = false;
88     int myfd;
89 
90     myfd = session->gpsdata.gps_fd;
91 
92     /* request firmware revision and look for a valid response */
93     putbe32(buf, 0, 0);
94     if (geostar_write(session, 0xc1, buf, 1) == 0) {
95 	unsigned int n;
96 	for (n = 0; n < 3; n++) {
97 	    if (!nanowait(myfd, NS_IN_SEC))
98 		break;
99 	    if (generic_get(session) >= 0) {
100 		if (session->lexer.type == GEOSTAR_PACKET) {
101 		    GPSD_LOG(LOG_RAW, &session->context->errout,
102 			     "geostar_detect found\n");
103 		    ret = true;
104 		    break;
105 		}
106 	    }
107 	}
108     }
109 
110     return ret;
111 }
112 
geostar_analyze(struct gps_device_t * session)113 static gps_mask_t geostar_analyze(struct gps_device_t *session)
114 {
115     int i, len;
116     gps_mask_t mask = 0;
117     unsigned int id;
118     uint16_t uw1, uw2;
119     uint32_t ul1, ul2, ul3, ul4, ul5;
120     double d1, d2, d3, d4, d5;
121     char buf[BUFSIZ];
122     char buf2[BUFSIZ];
123 
124     if (session->lexer.type != GEOSTAR_PACKET) {
125 	GPSD_LOG(LOG_INF, &session->context->errout,
126 		 "geostar_analyze packet type %d\n",
127 		 session->lexer.type);
128 	return 0;
129     }
130 
131     if (session->lexer.outbuflen < 12 || session->lexer.outbuffer[0] != 'P')
132 	return 0;
133 
134     /* put data part of message in buf */
135 
136     memset(buf, 0, sizeof(buf));
137     /* cppcheck-suppress redundantCopy */
138     memcpy(buf, session->lexer.outbuffer, session->lexer.outbuflen);
139 
140     buf2[len = 0] = '\0';
141     for (i = 0; i < (int)session->lexer.outbuflen; i++) {
142 	str_appendf(buf2, sizeof(buf2),
143 		       "%02x", buf[len++] = session->lexer.outbuffer[i]);
144     }
145 
146     id = (unsigned int)getleu16(session->lexer.outbuffer, OFFSET(0));
147 
148     GPSD_LOG(LOG_DATA, &session->context->errout,
149 	     "GeoStar packet id 0x%02x length %d: %s\n", id, len, buf2);
150 
151     session->cycle_end_reliable = true;
152 
153     switch (id) {
154     case 0x10:
155 	GPSD_LOG(LOG_INF, &session->context->errout, "Raw measurements\n");
156 	break;
157     case 0x11:
158 	GPSD_LOG(LOG_INF, &session->context->errout, "GPS sub-frame data\n");
159 	break;
160     case 0x12:
161 	GPSD_LOG(LOG_INF, &session->context->errout, "GLONASS sub-frame data\n");
162 	break;
163     case 0x13:
164 	d1 = getled64(buf, OFFSET(1));
165 	d2 = getled64(buf, OFFSET(3));
166 	d3 = getled64(buf, OFFSET(5));
167 	d4 = getled64(buf, OFFSET(29)); /* GPS time */
168 	d5 = getled64(buf, OFFSET(31)); /* GLONASS time */
169 	GPSD_LOG(LOG_INF, &session->context->errout,
170 		 "ECEF coordinates %g %g %g %f %f\n", d1, d2, d3, d4, d5);
171 	break;
172     case 0x20:
173 	d1 = getled64(buf, OFFSET(1)); /* time */
174 
175         DTOTS(&session->newdata.time, d1);
176         session->newdata.time.tv_sec += JAN_2008;
177 
178 	session->newdata.latitude = getled64(buf, OFFSET(3)) * RAD_2_DEG;
179 	session->newdata.longitude = getled64(buf, OFFSET(5)) * RAD_2_DEG;
180         /* altitude above ellipsoid */
181 	session->newdata.altHAE = getled64(buf, OFFSET(7));
182 	session->newdata.geoid_sep = getled64(buf, OFFSET(9));
183 	session->gpsdata.satellites_used = (int)getles32(buf, OFFSET(11));
184 	session->gpsdata.dop.gdop = getled64(buf, OFFSET(13));
185 	session->gpsdata.dop.pdop = getled64(buf, OFFSET(15));
186 	session->gpsdata.dop.tdop = getled64(buf, OFFSET(17));
187 	session->gpsdata.dop.hdop = getled64(buf, OFFSET(19));
188 	session->gpsdata.dop.vdop = getled64(buf, OFFSET(21));
189 	session->newdata.speed = getled64(buf, OFFSET(31));
190 	session->newdata.track = getled64(buf, OFFSET(33)) * RAD_2_DEG;
191 
192 	ul1 = getleu32(buf, OFFSET(29)); /* status */
193 
194 	if (ul1 != 0) {
195 	    session->gpsdata.status = STATUS_NO_FIX;
196 	    mask |= STATUS_SET;
197 	} else {
198 	    if (session->gpsdata.status < STATUS_FIX) {
199 		session->gpsdata.status = STATUS_FIX;
200 		mask |= STATUS_SET;
201 	    }
202 	}
203 	mask |= TIME_SET | NTPTIME_IS | LATLON_SET | ALTITUDE_SET |
204                 SPEED_SET | TRACK_SET | DOP_SET | USED_IS | REPORT_IS;
205 
206 	GPSD_LOG(LOG_INF, &session->context->errout,
207 		 "Geographic coordinates %f %g %g %g %g %g\n",
208 		 d1,
209 		 session->newdata.latitude,
210 		 session->newdata.longitude,
211 		 session->newdata.altHAE,
212 		 session->newdata.speed,
213 		 session->newdata.track);
214 	GPSD_LOG(LOG_INF, &session->context->errout,
215 		 "Dilution of precision %g %g %g %g %g\n",
216 		 session->gpsdata.dop.gdop,
217 		 session->gpsdata.dop.pdop,
218 		 session->gpsdata.dop.tdop,
219 		 session->gpsdata.dop.hdop,
220 		 session->gpsdata.dop.vdop);
221 	break;
222     case 0x21:
223 	ul1 = getleu32(buf, OFFSET(1));
224 	ul2 = getleu32(buf, OFFSET(2));
225 	uw1 = getleu16(buf, OFFSET(3));
226 	uw2 = getleu16(buf, OFFSET(3) + 2);
227 	GPSD_LOG(LOG_INF, &session->context->errout,
228 		 "Current receiver telemetry %x %d %d %d\n",
229 		 ul1, ul2, uw1, uw2);
230 	if(ul1 & (1<<3)) {
231 	    session->newdata.mode = MODE_2D;
232 	}
233 	else {
234 	    session->newdata.mode = MODE_3D;
235 	}
236 	if(ul1 & (1<<2)) {
237 	    session->gpsdata.status = STATUS_FIX;
238 	}
239 	else {
240 	    session->gpsdata.status = STATUS_NO_FIX;
241 	    session->newdata.mode = MODE_NO_FIX;
242 	}
243 
244 	mask |= MODE_SET | STATUS_SET;
245 	break;
246     case 0x22:
247 	ul1 = getleu32(buf, OFFSET(1));
248 	GPSD_LOG(LOG_INF, &session->context->errout, "SVs in view %d\n", ul1);
249 	session->gpsdata.satellites_visible = (int)ul1;
250 	if(ul1 > GEOSTAR_CHANNELS) ul1 = GEOSTAR_CHANNELS;
251 	for(i = 0; (uint32_t)i < ul1; i++) {
252 	    int16_t s1, s2, s3;
253 	    ul2 = getleu32(buf, OFFSET(2) + i * 3 * 4);
254 	    s1 = getles16(buf, OFFSET(3) + i * 3 * 4);
255 	    s2 = getles16(buf, OFFSET(3) + 2 + i * 3 * 4);
256 	    s3 = getles16(buf, OFFSET(4) + 2 + i * 3 * 4);
257 	    GPSD_LOG(LOG_INF, &session->context->errout,
258                      "ID %d Az %g El %g SNR %g\n",
259 		     decode_channel_id(ul2), s1*0.001*RAD_2_DEG,
260                      s2*0.001*RAD_2_DEG, s3*0.1);
261 	    session->gpsdata.skyview[i].PRN = (short)decode_channel_id(ul2);
262 	    session->gpsdata.skyview[i].azimuth = (short)round((double)s1*0.001 * RAD_2_DEG);
263 	    session->gpsdata.skyview[i].elevation = (short)round((double)s2*0.001 * RAD_2_DEG);
264 	    session->gpsdata.skyview[i].ss = (double)s3*0.1;
265 	    session->gpsdata.skyview[i].used = (bool)(ul2 & (1<<27));
266 	}
267 	session->gpsdata.skyview_time.tv_sec = 0;
268 	session->gpsdata.skyview_time.tv_nsec = 0;
269 	mask |= SATELLITE_SET | USED_IS;
270 	break;
271     case 0x3e:
272 	ul1 = getleu32(buf, OFFSET(1));
273 	ul2 = getleu32(buf, OFFSET(2));
274 	ul3 = getleu32(buf, OFFSET(3));
275 	GPSD_LOG(LOG_INF, &session->context->errout,
276 		 "Receiver power-up message %d %d %d\n", ul1, ul2, ul3);
277 	break;
278     case 0x3f:
279 	ul1 = getleu32(buf, OFFSET(1));
280 	ul2 = getleu32(buf, OFFSET(2));
281 	GPSD_LOG(LOG_WARN, &session->context->errout,
282 		 "Negative acknowledge %x %d\n", ul1, ul2);
283 	break;
284     case 0x40:
285 	GPSD_LOG(LOG_INF, &session->context->errout,
286 		 "Response to Set initial parameters\n");
287 	break;
288     case 0x41:
289 	GPSD_LOG(LOG_INF, &session->context->errout,
290 		 "Response to Set serial ports parameters\n");
291 	break;
292     case 0x42:
293 	ul1 = getleu32(buf, OFFSET(1));
294 	ul2 = getleu32(buf, OFFSET(2));
295 	ul3 = getleu32(buf, OFFSET(3));
296 	GPSD_LOG(LOG_INF, &session->context->errout,
297 		 "Response to Set receiver operation mode %d %d %d\n",
298 		 ul1, ul2, ul3);
299 	break;
300     case 0x43:
301 	GPSD_LOG(LOG_INF, &session->context->errout,
302 		 "Response to Set navigation task solution parameters\n");
303 	break;
304     case 0x44:
305 	GPSD_LOG(LOG_INF, &session->context->errout,
306 		 "Response to Set output data rate\n");
307 	break;
308     case 0x46:
309 	GPSD_LOG(LOG_INF, &session->context->errout,
310 		 "Response to Assign data protocol to communication port\n");
311 	break;
312     case 0x48:
313 	GPSD_LOG(LOG_INF, &session->context->errout,
314 		 "Response to Set GPS almanac\n");
315 	break;
316     case 0x49:
317 	GPSD_LOG(LOG_INF, &session->context->errout,
318 		 "Response to Set GLONASS almanac\n");
319 	break;
320     case 0x4a:
321 	GPSD_LOG(LOG_INF, &session->context->errout,
322 		 "Response to Set GPS ephemeris\n");
323 	break;
324     case 0x4b:
325 	GPSD_LOG(LOG_INF, &session->context->errout,
326 		 "Response to Set GLONASS ephemeris\n");
327 	break;
328     case 0x4c:
329 	ul1 = getleu32(buf, OFFSET(1));
330 	ul2 = getleu32(buf, OFFSET(2));
331 	ul3 = getleu32(buf, OFFSET(3));
332 	ul4 = getleu32(buf, OFFSET(4));
333 	ul5 = getleu32(buf, OFFSET(5));
334 	GPSD_LOG(LOG_INF, &session->context->errout,
335 		 "Response to Set PPS parameters %d %d %d %d %d\n",
336 		 ul1, ul2, ul3, ul4, ul5);
337 	break;
338     case 0x4d:
339 	GPSD_LOG(LOG_INF, &session->context->errout,
340 		 "Response to Enable/disable SV in position fix\n");
341 	break;
342     case 0x4e:
343 	GPSD_LOG(LOG_INF, &session->context->errout,
344 		 "Response to Enable/disable NMEA messages\n");
345 	break;
346     case 0x4f:
347 	ul1 = getleu32(buf, OFFSET(1));
348 	ul2 = getleu32(buf, OFFSET(2));
349 	GPSD_LOG(LOG_INF, &session->context->errout,
350 		 "Response to Enable/disable binary messages %x %x\n",
351 		 ul1, ul2);
352 	break;
353     case 0x80:
354 	GPSD_LOG(LOG_INF, &session->context->errout,
355 		 "Response to Query initial parameters\n");
356 	break;
357     case 0x81:
358 	GPSD_LOG(LOG_INF, &session->context->errout,
359 		 "Response to Query serial ports parameters\n");
360 	break;
361     case 0x82:
362 	ul1 = getleu32(buf, OFFSET(1));
363 	ul2 = getleu32(buf, OFFSET(2));
364 	ul3 = getleu32(buf, OFFSET(3));
365 	GPSD_LOG(LOG_INF, &session->context->errout,
366 		 "Response to Query receiver operation mode %d %d %d\n",
367 		 ul1, ul2, ul3);
368 	break;
369     case 0x83:
370 	GPSD_LOG(LOG_INF, &session->context->errout,
371 		 "Response to Query navigation task solution parameters\n");
372 	break;
373     case 0x84:
374 	GPSD_LOG(LOG_INF, &session->context->errout,
375 		 "Response to Query output data rate\n");
376 	break;
377     case 0x86:
378 	session->driver.geostar.physical_port = (unsigned int)getleu32(buf, OFFSET(1));
379 	GPSD_LOG(LOG_INF, &session->context->errout,
380 		 "Response to Query data protocol assignment to communication port\n");
381 	GPSD_LOG(LOG_INF, &session->context->errout,
382 		 "Connected to physical port %d\n",
383 		 session->driver.geostar.physical_port);
384 	break;
385     case 0x88:
386 	GPSD_LOG(LOG_INF, &session->context->errout,
387 		 "Response to Query GPS almanac\n");
388 	break;
389     case 0x89:
390 	GPSD_LOG(LOG_INF, &session->context->errout,
391 		 "Response to Query GLONASS almanac\n");
392 	break;
393     case 0x8a:
394 	GPSD_LOG(LOG_INF, &session->context->errout,
395 		 "Response to Query GPS ephemerides\n");
396 	break;
397     case 0x8b:
398 	d1 = getled64(buf, OFFSET(23));
399 	d2 = getled64(buf, OFFSET(25));
400 	d3 = getled64(buf, OFFSET(27));
401 	GPSD_LOG(LOG_INF, &session->context->errout,
402 		 "Response to Query GLONASS ephemerides %g %g %g\n",
403 		 d1, d2, d3);
404 	break;
405     case 0x8c:
406 	ul1 = getleu32(buf, OFFSET(1));
407 	ul2 = getleu32(buf, OFFSET(2));
408 	ul3 = getleu32(buf, OFFSET(3));
409 	ul4 = getleu32(buf, OFFSET(4));
410 	ul5 = getleu32(buf, OFFSET(5));
411 	GPSD_LOG(LOG_INF, &session->context->errout,
412 		 "Response to Query PPS parameters %d %d %d %d %d\n",
413 		 ul1, ul2, ul3, ul4, ul5);
414 	break;
415     case 0x8d:
416 	GPSD_LOG(LOG_INF, &session->context->errout,
417 		 "Response to Query enable/disable status of the SV in position fix\n");
418 	break;
419     case 0x8e:
420 	GPSD_LOG(LOG_INF, &session->context->errout,
421 		 "Response to Query enable NMEA messages\n");
422 	break;
423     case 0x8f:
424 	ul1 = getleu32(buf, OFFSET(1));
425 	ul2 = getleu32(buf, OFFSET(2));
426 	GPSD_LOG(LOG_INF, &session->context->errout,
427 		 "Response to Query enable binary messages %x %x\n",
428 		 ul1, ul2);
429 	break;
430     case 0xc0:
431 	GPSD_LOG(LOG_INF, &session->context->errout,
432 		 "Response to Change operation mode command\n");
433 	break;
434     case 0xc1:
435 	ul4 = getleu32(buf, OFFSET(1));
436 	ul1 = getleu32(buf, OFFSET(2));
437 	ul2 = getleu32(buf, OFFSET(3));
438 	ul3 = getleu32(buf, OFFSET(4));
439         (void)snprintf(session->subtype, sizeof(session->subtype),
440                 "%u.%u %u.%u.%u %x %c-%u\n",
441                 ul4>>16, ul4&0xFFFF, ul1>>9, (ul1>>5)&0xF, ul1&0x1F, ul2,
442                 ul3>>24, ul3&0x00FFFFFF);
443 	GPSD_LOG(LOG_INF, &session->context->errout,
444 		 "Response to Request FW version command: %s\n",
445 		 session->subtype);
446 	mask |= DEVICEID_SET;
447 	break;
448     case 0xc2:
449 	GPSD_LOG(LOG_INF, &session->context->errout,
450 		 "Response to Restart receiver command\n");
451 	break;
452     case 0xc3:
453 	GPSD_LOG(LOG_INF, &session->context->errout,
454 		 "Response to Store parameters to Flash command\n");
455 	break;
456     case 0xd0:
457 	GPSD_LOG(LOG_INF, &session->context->errout,
458 		 "Response to Erase Flash sector command\n");
459 	break;
460     case 0xd1:
461 	GPSD_LOG(LOG_INF, &session->context->errout,
462 		 "Response to Write data to Flash command\n");
463 	break;
464     case 0xd2:
465 	GPSD_LOG(LOG_INF, &session->context->errout,
466 		 "Response to Store Serial Number command\n");
467 	break;
468     default:
469 	GPSD_LOG(LOG_WARN, &session->context->errout,
470 		 "Unhandled GeoStar packet type 0x%02x\n", id);
471 	break;
472     }
473 
474     return mask;
475 }
476 
geostar_parse_input(struct gps_device_t * session)477 static gps_mask_t geostar_parse_input(struct gps_device_t *session)
478 {
479     if (session->lexer.type == GEOSTAR_PACKET) {
480 	return geostar_analyze(session);;
481     } else
482 	return 0;
483 }
484 
485 #ifdef CONTROLSEND_ENABLE
geostar_control_send(struct gps_device_t * session,char * buf,size_t buflen)486 static ssize_t geostar_control_send(struct gps_device_t *session,
487 				 char *buf, size_t buflen)
488 /* not used by the daemon, it's for gpsctl and friends */
489 {
490     return (ssize_t) geostar_write(session,
491 				(unsigned int)buf[0],
492 				(unsigned char *)buf + 1, (buflen - 1)/4);
493 }
494 #endif /* CONTROLSEND_ENABLE */
495 
496 
geostar_init_query(struct gps_device_t * session)497 static void geostar_init_query(struct gps_device_t *session)
498 {
499     unsigned char buf[2 * 4];
500     /* Poll Software Version */
501     (void)geostar_write(session, 0xc1, buf, 1);
502 }
503 
geostar_event_hook(struct gps_device_t * session,event_t event)504 static void geostar_event_hook(struct gps_device_t *session, event_t event)
505 {
506     unsigned char buf[2 * 4];
507 
508     if (session->context->readonly)
509 	return;
510 
511     if (event == event_identified || event == event_reactivate) {
512 	/* Select binary packets */
513 	putbe32(buf, 0, 0xffff0000);
514 	putbe32(buf, 4, 0);
515 	(void)geostar_write(session, 0x4f, buf, 2);
516 
517 	/* Poll Ports params */
518 	putbe32(buf, 0, 1);
519 	(void)geostar_write(session, 0x81, buf, 1);
520 	putbe32(buf, 0, 0);
521 	(void)geostar_write(session, 0x81, buf, 1);
522 	/* Poll Init params */
523 	(void)geostar_write(session, 0x80, buf, 1);
524 	/* Poll Mode */
525 	(void)geostar_write(session, 0x82, buf, 1);
526 	/* Poll Solution params */
527 	(void)geostar_write(session, 0x83, buf, 1);
528 	/* Poll Output rate */
529 	(void)geostar_write(session, 0x84, buf, 1);
530 	/* Poll Protocols assignment */
531 	(void)geostar_write(session, 0x86, buf, 1);
532 	/* Poll PPS params */
533 	(void)geostar_write(session, 0x8c, buf, 1);
534 	/* Poll NMEA packets selected */
535 	(void)geostar_write(session, 0x8e, buf, 1);
536 	/* Poll binary packets selected */
537 	(void)geostar_write(session, 0x8f, buf, 1);
538     }
539 
540     if (event == event_deactivate) {
541 	/* Perform cold restart */
542 	putbe32(buf, 0, 3);
543 	(void)geostar_write(session, 0xc2, buf, 1);
544     }
545 }
546 
547 #ifdef RECONFIGURE_ENABLE
geostar_speed_switch(struct gps_device_t * session,speed_t speed,char parity,int stopbits)548 static bool geostar_speed_switch(struct gps_device_t *session,
549 			      speed_t speed, char parity, int stopbits)
550 {
551     unsigned char buf[4 * 4];
552     int iparity;
553 
554     switch (parity) {
555     case 'E':
556     case 2:
557 	parity = (char)2;
558 	break;
559     case 'O':
560     case 1:
561 	parity = (char)1;
562 	break;
563     case 'N':
564     case 0:
565     default:
566 	parity = (char)0;
567 	break;
568     }
569     iparity = parity;
570 
571     putbe32(buf, 0, session->driver.geostar.physical_port);
572     putbe32(buf, 4, speed);
573     putbe32(buf, 8, stopbits);
574     putbe32(buf, 12, iparity);
575     (void)geostar_write(session, 0x41, buf, 4);
576 
577     return true;	/* it would be nice to error-check this */
578 }
579 
geostar_mode(struct gps_device_t * session,int mode)580 static void geostar_mode(struct gps_device_t *session, int mode)
581 {
582     if (mode == MODE_NMEA) {
583 	unsigned char buf[1 * 4];
584 	/* Switch to NMEA mode */
585 	putbe32(buf, 0, 1);
586 	(void)geostar_write(session, 0x46, buf, 1);
587     } else if (mode == MODE_BINARY) {
588 	/* Switch to binary mode */
589 	(void)nmea_send(session, "$GPSGG,SWPROT");
590     } else {
591 	GPSD_LOG(LOG_ERROR, &session->context->errout,
592 		 "unknown mode %i requested\n", mode);
593     }
594 }
595 #endif /* RECONFIGURE_ENABLE */
596 
geostar_time_offset(struct gps_device_t * session UNUSED)597 static double geostar_time_offset(struct gps_device_t *session UNUSED)
598 {
599     return 0.31;
600 }
601 
602 /* this is everything we export */
603 /* *INDENT-OFF* */
604 const struct gps_type_t driver_geostar =
605 {
606     .type_name      = "GeoStar",	/* full name of type */
607     .packet_type    = GEOSTAR_PACKET,	/* associated lexer packet type */
608     .flags	    = DRIVER_STICKY,	/* remember this */
609     .trigger	    = NULL,		/* no trigger */
610     .channels       = GEOSTAR_CHANNELS,	/* consumer-grade GPS/GLONASS */
611     .probe_detect   = geostar_detect,	/* probe for device */
612     .get_packet     = generic_get,	/* use the generic packet getter */
613     .parse_packet   = geostar_parse_input,	/* parse message packets */
614     .rtcm_writer    = NULL,		/* doesn't accept DGPS corrections */
615     .init_query     = geostar_init_query,	/* non-perturbing initial query */
616     .event_hook     = geostar_event_hook,	/* fire on various lifetime events */
617 #ifdef RECONFIGURE_ENABLE
618     .speed_switcher = geostar_speed_switch,/* change baud rate */
619     .mode_switcher  = geostar_mode,	/* there is a mode switcher */
620     .rate_switcher  = NULL,		/* no rate switcher */
621     .min_cycle.tv_sec  = 1,		/* not relevant, no rate switch */
622     .min_cycle.tv_nsec = 0,		/* not relevant, no rate switch */
623 #endif /* RECONFIGURE_ENABLE */
624 #ifdef CONTROLSEND_ENABLE
625     .control_send   = geostar_control_send,/* how to send commands */
626 #endif /* CONTROLSEND_ENABLE */
627     .time_offset     = geostar_time_offset,
628 };
629 /* *INDENT-ON* */
630 
631 #endif /* GEOSTAR_ENABLE */
632