1 /*
2 * A prototype driver. Doesn't run, doesn't even compile.
3 *
4 * For new driver authors: replace "_PROTO_" and "_proto_" with the name of
5 * your new driver. That will give you a skeleton with all the required
6 * functions defined.
7 *
8 * Once that is done, you will likely have to define a large number of
9 * flags and masks. From there, you will be able to start extracting
10 * useful quantities. There are roughed-in decoders for the navigation
11 * solution, satellite status and gps-utc offset. These are the 3 key
12 * messages that gpsd needs. Some protocols transmit error estimates
13 * separately from the navigation solution; if developing a driver for
14 * such a protocol you will need to add a decoder function for that
15 * message. Be extra careful when using sizeof(<type>) to extract part
16 * of packets (ie. don't do it). This idiom creates portability problems
17 * between 32 and 64 bit systems.
18 *
19 * For anyone hacking this driver skeleton: "_PROTO_" and "_proto_" are now
20 * reserved tokens. We suggest that they only ever be used as prefixes,
21 * but if they are used infix, they must be used in a way that allows a
22 * driver author to find-and-replace to create a unique namespace for
23 * driver functions.
24 *
25 * If using vi, ":%s/_PROTO_/MYDRIVER/g" and ":%s/_proto_/mydriver/g"
26 * should produce a source file that comes very close to being useful.
27 * You will also need to add hooks for your new driver to:
28 * SConstruct
29 * drivers.c
30 * gpsd.h-tail
31 * libgpsd_core.c
32 * packet.c
33 * packet_states.h
34 *
35 * This file is Copyright (c) 2010-2018 by the GPSD project
36 * SPDX-License-Identifier: BSD-2-clause
37 */
38
39 #include "gpsd_config.h" /* must be before all includes */
40
41 #include <stdio.h>
42 #include <stdbool.h>
43 #include <string.h>
44
45 #if defined(_PROTO__ENABLE) && defined(BINARY_ENABLE)
46
47 #include "bits.h"
48
49 static gps_mask_t _proto__parse_input(struct gps_device_t *);
50 static gps_mask_t _proto__dispatch(struct gps_device_t *, unsigned char *, size_t );
51 static gps_mask_t _proto__msg_navsol(struct gps_device_t *, unsigned char *, size_t );
52 static gps_mask_t _proto__msg_utctime(struct gps_device_t *, unsigned char *, size_t );
53 static gps_mask_t _proto__msg_svinfo(struct gps_device_t *, unsigned char *, size_t );
54 static gps_mask_t _proto__msg_raw(struct gps_device_t *, unsigned char *, size_t );
55
56 /*
57 * These methods may be called elsewhere in gpsd
58 */
59 static ssize_t _proto__control_send(struct gps_device_t *, char *, size_t);
60 static bool _proto__probe_detect(struct gps_device_t *);
61 static void _proto__event_hook(struct gps_device_t *, event_t);
62 static bool _proto__set_speed(struct gps_device_t *, speed_t, char, int);
63 static void _proto__set_mode(struct gps_device_t *, int);
64
65 /*
66 * Decode the navigation solution message
67 */
68 static gps_mask_t
_proto__msg_navsol(struct gps_device_t * session,unsigned char * buf,size_t data_len)69 _proto__msg_navsol(struct gps_device_t *session, unsigned char *buf, size_t data_len)
70 {
71 gps_mask_t mask;
72 int flags;
73 double Px, Py, Pz, Vx, Vy, Vz;
74
75 if (data_len != _PROTO__NAVSOL_MSG_LEN)
76 return 0;
77
78 GPSD_LOG(LOG_DATA, &session->context->errout,
79 "_proto_ NAVSOL - navigation data\n");
80 /* if this protocol has a way to test message validity, use it */
81 flags = GET_FLAGS();
82 if ((flags & _PROTO__SOLUTION_VALID) == 0)
83 return 0;
84
85 mask = ONLINE_SET;
86
87 /* extract ECEF navigation solution here */
88 /* or extract the local tangential plane (ENU) solution */
89 [session->newdata.ecef.x,
90 session->newdata.ecef.y,
91 session->newdata.ecef.z,
92 session->newdata.ecef.vx,
93 session->newdata.ecef.vy,
94 session->newdata.ecef.vz] = GET_ECEF_FIX();
95 mask |= ECEF_SET | VECEF_SET;
96
97 session->newdata.epx = GET_LONGITUDE_ERROR();
98 session->newdata.epy = GET_LATITUDE_ERROR();
99 session->newdata.eps = GET_SPEED_ERROR();
100 session->gpsdata.satellites_used = GET_SATELLITES_USED();
101 /*
102 * Do *not* clear DOPs in a navigation solution message;
103 * instead, opportunistically pick up whatever it gives
104 * us and replace whatever values we computed from the
105 * visibility matrix for he last skyview. The reason to trust
106 * the chip returns over what we compute is that some
107 * chips have internal deweighting albums to throw out sats
108 * that increase DOP.
109 */
110 session->gpsdata.dop.hdop = GET_HDOP();
111 session->gpsdata.dop.vdop = GET_VDOP();
112 /* other DOP if available */
113 mask |= DOP_SET;
114
115 session->newdata.mode = GET_FIX_MODE();
116 session->gpsdata.status = GET_FIX_STATUS();
117
118 /*
119 * Mix in CLEAR_IS to clue the daemon in about when to clear fix
120 * information. Mix in REPORT_IS when the sentence is reliably
121 * the last in a reporting cycle.
122 */
123 mask |= MODE_SET | STATUS_SET | REPORT_IS;
124
125 /*
126 * At the end of each packet-cracking function, report at LOG_DATA level
127 * the fields it potentially set and the transfer mask. Doing this
128 * makes it relatively easy to track down data-management problems.
129 */
130 GPSD_LOG(LOG_DATA, &session->context->errout,
131 "NAVSOL: time=%.2f, ecef x:%.2f y: %.2f z: %.2f mode=%d "
132 "status=%d\n",
133 session->newdata.time,
134 session->newdata.ecef.x,
135 session->newdata.ecef.y,
136 session->newdata.ecef.z,
137 session->newdata.longitude,
138 session->newdata.mode,
139 session->gpsdata.status);
140
141 return mask;
142 }
143
144 /**
145 * GPS Leap Seconds
146 */
147 static gps_mask_t
_proto__msg_utctime(struct gps_device_t * session,unsigned char * buf,size_t data_len)148 _proto__msg_utctime(struct gps_device_t *session, unsigned char *buf, size_t data_len)
149 {
150 double t;
151
152 if (data_len != UTCTIME_MSG_LEN)
153 return 0;
154
155 GPSD_LOG(LOG_DATA, &session->context->errout,
156 "_proto_ UTCTIME - navigation data\n");
157 /* if this protocol has a way to test message validity, use it */
158 flags = GET_FLAGS();
159 if ((flags & _PROTO__TIME_VALID) == 0)
160 return 0;
161
162 tow = GET_MS_TIMEOFWEEK();
163 gps_week = GET_WEEKNUMBER();
164 session->context->leap_seconds = GET_GPS_LEAPSECONDS();
165 session->newdata.time = gpsd_gpstime_resolv(session, gps_week, tow);
166
167 return TIME_SET | NTPTIME_IS | ONLINE_SET;
168 }
169
170 /**
171 * GPS Satellite Info
172 */
173 static gps_mask_t
_proto__msg_svinfo(struct gps_device_t * session,unsigned char * buf,size_t data_len)174 _proto__msg_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len)
175 {
176 unsigned char i, st, nchan, nsv;
177 unsigned int tow;
178
179 if (data_len != SVINFO_MSG_LEN )
180 return 0;
181
182 GPSD_LOG(LOG_DATA, &session->context->errout,
183 "_proto_ SVINFO - navigation data\n");
184 /* if this protocol has a way to test message validity, use it */
185 flags = GET_FLAGS();
186 if ((flags & _PROTO__SVINFO_VALID) == 0)
187 return 0;
188
189 /*
190 * some protocols have a variable length message listing only visible
191 * satellites, even if there are less than the number of channels. others
192 * have a fixed length message and send empty records for idle channels
193 * that are not tracking or searching. whatever the case, nchan should
194 * be set to the number of satellites which might be visible.
195 */
196 nchan = GET_NUMBER_OF_CHANNELS();
197 if ((nchan < 1) || (nchan > MAXCHANNELS)) {
198 GPSD_LOG(LOG_INF, &session->context->errout,
199 "too many channels reported\n");
200 return 0;
201 }
202 gpsd_zero_satellites(&session->gpsdata);
203 nsv = 0; /* number of actually used satellites */
204 for (i = st = 0; i < nchan; i++) {
205 /* get info for one channel/satellite */
206 int off = GET_CHANNEL_STATUS(i);
207
208 session->gpsdata.PRN[i] = PRN_THIS_CHANNEL_IS_TRACKING(i);
209 session->gpsdata.ss[i] = (float)SIGNAL_STRENGTH_FOR_CHANNEL(i);
210 session->gpsdata.elevation[i] = SV_ELEVATION_FOR_CHANNEL(i);
211 session->gpsdata.azimuth[i] = SV_AZIMUTH_FOR_CHANNEL(i);
212
213 if (CHANNEL_USED_IN_SOLUTION(i))
214 session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
215
216 if(session->gpsdata.PRN[i])
217 st++;
218 }
219 /* if the satellite-info setence gives you UTC time, use it */
220 session->gpsdata.skyview_time = NaN;
221 session->gpsdata.satellites_used = nsv;
222 session->gpsdata.satellites_visible = st;
223 GPSD_LOG(LOG_DATA, &session->context->errout,
224 "SVINFO: visible=%d used=%d mask={SATELLITE|USED}\n",
225 session->gpsdata.satellites_visible,
226 session->gpsdata.satellites_used);
227 return SATELLITE_SET | USED_IS;
228 }
229
230 /**
231 * Raw measurements
232 */
233 static gps_mask_t
_proto__msg_raw(struct gps_device_t * session,unsigned char * buf,size_t data_len)234 _proto__msg_raw(struct gps_device_t *session, unsigned char *buf, size_t data_len)
235 {
236 unsigned char i, st, nchan, nsv;
237 unsigned int tow;
238
239 if (data_len != RAW_MSG_LEN )
240 return 0;
241
242 GPSD_LOG(LOG_DATA, &session->context->errout,
243 "_proto_ RAW - raw measurements\n");
244 /* if this protocol has a way to test message validity, use it */
245 flags = GET_FLAGS();
246 if ((flags & _PROTO__SVINFO_VALID) == 0)
247 return 0;
248
249 /*
250 * not all chipsets emit the same information. some of these observables
251 * can be easily converted into others. these are suggestions for the
252 * quantities you may wish to try extract. chipset documentation may say
253 * something like "this message contains information required to generate
254 * a RINEX file." assign NAN for unavailable data.
255 */
256 nchan = GET_NUMBER_OF_CHANNELS();
257 if ((nchan < 1) || (nchan > MAXCHANNELS)) {
258 GPSD_LOG(LOG_INF, &session->context->errout,
259 "too many channels reported\n");
260 return 0;
261 }
262
263 DTONS(&session->raw.mtime, GET_TIME());
264
265 /* this is so we can tell which never got set */
266 for (i = 0; i < MAXCHANNELS; i++)
267 session->gpsdata.raw.meas[i].svid = 0;
268 for (i = 0; i < n; i++){
269 session->gpsdata.PRN[i] = GET_PRN();
270 session->gpsdata.ss[i] = GET_SIGNAL()
271 session->gpsdata.raw.meas[i].satstat = GET_FLAGS();
272 session->gpsdata.raw.meas[i].pseudorange = GET_PSEUDORANGE();
273 session->gpsdata.raw.meas[i].doppler = GET_DOPPLER();
274 session->gpsdata.raw.meas[i].carrierphase = GET_CARRIER_PHASE();
275 session->gpsdata.raw.meas[i].codephase = GET_CODE_PHASE();
276 session->gpsdata.raw.meas[i].deltarange = GET_DELTA_RANGE();
277 }
278 return RAW_IS;
279 }
280
281 /**
282 * Parse the data from the device
283 */
_proto__dispatch(struct gps_device_t * session,unsigned char * buf,size_t len)284 gps_mask_t _proto__dispatch(struct gps_device_t *session, unsigned char *buf, size_t len)
285 {
286 size_t i;
287 int type, used, visible, retmask = 0;
288
289 if (len == 0)
290 return 0;
291
292 /*
293 * Set this if the driver reliably signals end of cycle.
294 * The core library zeroes it just before it calls each driver's
295 * packet analyzer.
296 */
297 session->cycle_end_reliable = true;
298 if (msgid == MY_START_OF_CYCLE)
299 retmask |= CLEAR_IS;
300 else if (msgid == MY_END_OF_CYCLE)
301 retmask |= REPORT_IS;
302
303 type = GET_MESSAGE_TYPE();
304
305 /* we may need to dump the raw packet */
306 GPSD_LOG(LOG_RAW, &session->context->errout,
307 "raw _proto_ packet type 0x%02x\n", type);
308
309 switch (type)
310 {
311 /* Deliver message to specific decoder based on message type */
312
313 default:
314 GPSD_LOG(LOG_WARN, &session->context->errout,
315 "unknown packet id %d length %d\n", type, len);
316 return 0;
317 }
318 }
319
320 /**********************************************************
321 *
322 * Externally called routines below here
323 *
324 **********************************************************/
325
_proto__probe_detect(struct gps_device_t * session)326 static bool _proto__probe_detect(struct gps_device_t *session)
327 {
328 /*
329 * This method is used to elicit a positively identifying
330 * response from a candidate device. Some drivers may use
331 * this to test for the presence of a certain kernel module.
332 */
333 int test, satisfied;
334
335 /* Your testing code here */
336 test=satisfied=0;
337 if (test==satisfied)
338 return true;
339 return false;
340 }
341
342 #ifdef CONTROLSEND_ENABLE
343 /**
344 * Write data to the device, doing any required padding or checksumming
345 */
_proto__control_send(struct gps_device_t * session,char * msg,size_t msglen)346 static ssize_t _proto__control_send(struct gps_device_t *session,
347 char *msg, size_t msglen)
348 {
349 bool ok;
350
351 /* CONSTRUCT THE MESSAGE */
352
353 /*
354 * This copy to a public assembly buffer
355 * enables gpsmon to snoop the control message
356 * after it has been sent.
357 */
358 session->msgbuflen = msglen;
359 (void)memcpy(session->msgbuf, msg, msglen);
360
361 /* we may need to dump the message */
362 GPSD_LOG(LOG_PROG, &session->context->errout,
363 "writing _proto_ control type %02x\n");
364 return gpsd_write(session, session->msgbuf, session->msgbuflen);
365 }
366 #endif /* CONTROLSEND_ENABLE */
367
368 #ifdef RECONFIGURE_ENABLE
_proto__event_hook(struct gps_device_t * session,event_t event)369 static void _proto__event_hook(struct gps_device_t *session, event_t event)
370 {
371 if (session->context->readonly)
372 return;
373
374 if (event == event_wakeup) {
375 /*
376 * Code to make the device ready to communicate. Only needed if the
377 * device is in some kind of sleeping state, and only shipped to
378 * RS232C, so that gpsd won't send strings to unidentified USB devices
379 * that might not be GPSes at all.
380 */
381 }
382 if (event == event_identified) {
383 /*
384 * Fires when the first full packet is recognized from a
385 * previously unidentified device. The session.lexer counter
386 * is zeroed. If your device has a default cycle time other
387 * than 1 second, set session->device->gpsdata.cycle here. If
388 * possible, get the software version and store it in
389 * session->subtype.
390 */
391 }
392 if (event == event_configure) {
393 /*
394 * Change sentence mix and set reporting modes as needed.
395 * Called immediately after event_identified fires, then just
396 * after every packet received thereafter, but you probably
397 * only want to take actions on the first few packets after
398 * the session.lexer counter has been zeroed,
399 *
400 * Remember that session->lexer.counter is available when you
401 * write this hook; you can use this fact to interleave configuration
402 * sends with the first few packet reads, which is useful for
403 * devices with small receive buffers.
404 */
405 } else if (event == event_driver_switch) {
406 /*
407 * Fires when the driver on a device is changed *after* it
408 * has been identified.
409 */
410 } else if (event == event_deactivate) {
411 /*
412 * Fires when the device is deactivated. Usr this to revert
413 * whatever was done at event_identify and event_configure
414 * time.
415 */
416 } else if (event == event_reactivate) {
417 /*
418 * Fires when a device is reactivated after having been closed.
419 * Use this hook for re-establishing device settings that
420 * it doesn't hold through closes.
421 */
422 }
423 }
424
425 /*
426 * This is the entry point to the driver. When the packet sniffer recognizes
427 * a packet for this driver, it calls this method which passes the packet to
428 * the binary processor or the nmea processor, depending on the session type.
429 */
_proto__parse_input(struct gps_device_t * session)430 static gps_mask_t _proto__parse_input(struct gps_device_t *session)
431 {
432 if (session->lexer.type == _PROTO__PACKET) {
433 return _proto__dispatch(session, session->lexer.outbuffer, session->lexer.outbuflen);
434 #ifdef NMEA0183_ENABLE
435 } else if (session->lexer.type == NMEA_PACKET) {
436 return nmea_parse((char *)session->lexer.outbuffer, session);
437 #endif /* NMEA0183_ENABLE */
438 } else
439 return 0;
440 }
441
_proto__set_speed(struct gps_device_t * session,speed_t speed,char parity,int stopbits)442 static bool _proto__set_speed(struct gps_device_t *session,
443 speed_t speed, char parity, int stopbits)
444 {
445 /*
446 * Set port operating mode, speed, parity, stopbits etc. here.
447 * Note: parity is passed as 'N'/'E'/'O', but you should program
448 * defensively and allow 0/1/2 as well.
449 */
450 }
451
452 /*
453 * Switch between NMEA and binary mode, if supported
454 */
_proto__set_mode(struct gps_device_t * session,int mode)455 static void _proto__set_mode(struct gps_device_t *session, int mode)
456 {
457 if (mode == MODE_NMEA) {
458 /* send a mode switch control string */
459 } else {
460 /* send a mode switch control string */
461 }
462 }
463 #endif /* RECONFIGURE_ENABLE */
464
_proto_time_offset(struct gps_device_t * session)465 static double _proto_time_offset(struct gps_device_t *session)
466 {
467 /*
468 * If NTP notification is enabled, the GPS will occasionally NTP
469 * its notion of the time. This will lag behind actual time by
470 * some amount which has to be determined by observation vs. (say
471 * WWVB radio broadcasts) and, furthermore, may differ by baud
472 * rate. This method is for computing the NTP fudge factor. If
473 * it's absent, an offset of 0.0 will be assumed, effectively
474 * falling back on what's in ntp.conf. When it returns NAN,
475 * nothing will be sent to NTP.
476 */
477 return MAGIC_CONSTANT;
478 }
479
_proto__wrapup(struct gps_device_t * session)480 static void _proto__wrapup(struct gps_device_t *session)
481 {
482 }
483
484 /* The methods in this code take parameters and have */
485 /* return values that conform to the requirements AT */
486 /* THE TIME THE CODE WAS WRITTEN. */
487 /* */
488 /* These values may well have changed by the time */
489 /* you read this and methods could have been added */
490 /* or deleted. Unused methods can be set to NULL. */
491 /* */
492 /* The latest version can be found by inspecting */
493 /* the contents of struct gps_type_t in gpsd.h. */
494 /* */
495 /* This always contains the correct definitions that */
496 /* any driver must use to compile. */
497
498 /* This is everything we export */
499 /* *INDENT-OFF* */
500 const struct gps_type_t driver__proto__binary = {
501 /* Full name of type */
502 .type_name = "_proto",
503 /* Associated lexer packet type */
504 .packet_type = _PROTO__PACKET,
505 /* Driver tyoe flags */
506 .flags = DRIVER_NOFLAGS,
507 /* Response string that identifies device (not active) */
508 .trigger = NULL,
509 /* Number of satellite channels supported by the device */
510 .channels = 12,
511 /* Startup-time device detector */
512 .probe_detect = _proto__probe_detect,
513 /* Packet getter (using default routine) */
514 .get_packet = generic_get,
515 /* Parse message packets */
516 .parse_packet = _proto__parse_input,
517 /* RTCM handler (using default routine) */
518 .rtcm_writer = pass_rtcm,
519 /* non-perturbing initial query (e.g. for version) */
520 .init_query = NULL,
521 /* fire on various lifetime events */
522 .event_hook = _proto__event_hook,
523 #ifdef RECONFIGURE_ENABLE
524 /* Speed (baudrate) switch */
525 .speed_switcher = _proto__set_speed,
526 /* Switch to NMEA mode */
527 .mode_switcher = _proto__set_mode,
528 /* Message delivery rate switcher (not active) */
529 .rate_switcher = NULL,
530 /* Minimum cycle time of the device */
531 .min_cycle = 1,
532 #endif /* RECONFIGURE_ENABLE */
533 #ifdef CONTROLSEND_ENABLE
534 /* Control string sender - should provide checksum and headers/trailer */
535 .control_send = _proto__control_send,
536 #endif /* CONTROLSEND_ENABLE */
537 .time_offset = _proto_time_offset,
538 /* *INDENT-ON* */
539 };
540 #endif /* defined(_PROTO__ENABLE) && defined(BINARY_ENABLE) */
541
542