2 * A prototype driver. Doesn't run, doesn't even compile.
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
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
17 * For anyone hacking this driver skeleton: "_PROTO_" and "_proto_" are now
18 * reserved tokens. We suggest that they only ever be used as prefixes,
19 * but if they are used infix, they must be used in a way that allows a
20 * driver author to find-and-replace to create a unique namespace for
23 * If using vi, ":%s/_PROTO_/MYDRIVER/g" and ":%s/_proto_/mydriver/g"
24 * should produce a source file that comes very close to being useful.
25 * You will also need to add hooks for your new driver to:
33 * This file is Copyright (c) 2010 by the GPSD project
34 * BSD terms apply: see the file COPYING in the distribution root for details.
37 #include <sys/types.h>
45 #endif /* S_SPLINT_S */
50 #if defined(_PROTO__ENABLE) && defined(BINARY_ENABLE)
54 static gps_mask_t _proto__parse_input(struct gps_device_t *);
55 static gps_mask_t _proto__dispatch(struct gps_device_t *, unsigned char *, size_t );
56 static gps_mask_t _proto__msg_navsol(struct gps_device_t *, unsigned char *, size_t );
57 static gps_mask_t _proto__msg_utctime(struct gps_device_t *, unsigned char *, size_t );
58 static gps_mask_t _proto__msg_svinfo(struct gps_device_t *, unsigned char *, size_t );
59 static gps_mask_t _proto__msg_raw(struct gps_device_t *, unsigned char *, size_t );
62 * These methods may be called elsewhere in gpsd
64 static ssize_t _proto__control_send(struct gps_device_t *, char *, size_t);
65 static bool _proto__probe_detect(struct gps_device_t *);
66 static void _proto__event_hook(struct gps_device_t *, event_t);
67 static bool _proto__set_speed(struct gps_device_t *, speed_t, char, int);
68 static void _proto__set_mode(struct gps_device_t *, int);
71 * Decode the navigation solution message
74 _proto__msg_navsol(struct gps_device_t *session, unsigned char *buf, size_t data_len)
78 double Px, Py, Pz, Vx, Vy, Vz;
80 if (data_len != _PROTO__NAVSOL_MSG_LEN)
83 gpsd_report(LOG_IO, "_proto_ NAVSOL - navigation data\n");
84 /* if this protocol has a way to test message validity, use it */
86 if ((flags & _PROTO__SOLUTION_VALID) == 0)
91 /* extract ECEF navigation solution here */
92 /* or extract the local tangential plane (ENU) solution */
93 [Px, Py, Pz, Vx, Vy, Vz] = GET_ECEF_FIX();
94 ecef_to_wgs84fix(&session->newdata, &session->separation,
95 Px, Py, Pz, Vx, Vy, Vz);
96 mask |= LATLON_IS | ALTITUDE_IS | SPEED_IS | TRACK_IS | CLIMB_IS ;
98 session->newdata.epx = GET_LONGITUDE_ERROR();
99 session->newdata.epy = GET_LATITUDE_ERROR();
100 session->newdata.eps = GET_SPEED_ERROR();
101 session->gpsdata.satellites_used = GET_SATELLITES_USED();
103 * Do *not* clear DOPs in a navigation solution message;
104 * instead, opportunistically pick up whatever it gives
105 * us and replace whatever values we computed from the
106 * visibility matrix for he last skyview. The reason to trust
107 * the chip returns over what we compute is that some
108 * chips have internal deweighting albums to throw out sats
111 session->gpsdata.dop.hdop = GET_HDOP();
112 session->gpsdata.dop.vdop = GET_VDOP();
113 /* other DOP if available */
116 session->newdata.mode = GET_FIX_MODE();
117 session->gpsdata.status = GET_FIX_STATUS();
120 * Mix in CLEAR_IS to clue the daemon in about when to clear fix
121 * information. Mix in REPORT_IS when the sentence is reliably
122 * the last in a reporting cycle.
124 mask |= MODE_IS | STATUS_IS | REPORT_IS;
127 * At the end of each packet-cracking function, report at LOG_DATA level
128 * the fields it potentially set and the transfer mask. Doing this
129 * makes it relatively easy to track down data-management problems.
131 gpsd_report(LOG_DATA, "NAVSOL: time=%.2f, lat=%.2f lon=%.2f alt=%.2f mode=%d status=%d mask=%s\n",
132 session->newdata.time,
133 session->newdata.latitude,
134 session->newdata.longitude,
135 session->newdata.altitude,
136 session->newdata.mode,
137 session->gpsdata.status,
138 gpsd_maskdump(mask));
147 _proto__msg_utctime(struct gps_device_t *session, unsigned char *buf, size_t data_len)
151 if (data_len != UTCTIME_MSG_LEN)
154 gpsd_report(LOG_IO, "_proto_ UTCTIME - navigation data\n");
155 /* if this protocol has a way to test message validity, use it */
157 if ((flags & _PROTO__TIME_VALID) == 0)
160 tow = GET_MS_TIMEOFWEEK();
161 gps_week = GET_WEEKNUMBER();
162 session->context->gps_week = gps_week;
163 session->context->leap_seconds = GET_GPS_LEAPSECONDS();
164 session->context->gps_tow = tow / 1000.0;
166 t = gpstime_to_unix(gps_week, session->context->gps_tow)
167 - session->context->leap_seconds;
168 session->newdata.time = t;
170 return TIME_IS | ONLINE_IS;
177 _proto__msg_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len)
179 unsigned char i, st, nchan, nsv;
182 if (data_len != SVINFO_MSG_LEN )
185 gpsd_report(LOG_IO, "_proto_ SVINFO - navigation data\n");
186 /* if this protocol has a way to test message validity, use it */
188 if ((flags & _PROTO__SVINFO_VALID) == 0)
192 * some protocols have a variable length message listing only visible
193 * satellites, even if there are less than the number of channels. others
194 * have a fixed length message and send empty records for idle channels
195 * that are not tracking or searching. whatever the case, nchan should
196 * be set to the number of satellites which might be visible.
198 nchan = GET_NUMBER_OF_CHANNELS();
199 if ((nchan < 1) || (nchan > MAXCHANNELS)) {
200 gpsd_report(LOG_INF, "too many channels reported\n");
203 gpsd_zero_satellites(&session->gpsdata);
204 nsv = 0; /* number of actually used satellites */
205 for (i = st = 0; i < nchan; i++) {
206 /* get info for one channel/satellite */
207 int off = GET_CHANNEL_STATUS(i);
209 session->gpsdata.PRN[i] = PRN_THIS_CHANNEL_IS_TRACKING(i);
210 session->gpsdata.ss[i] = (float)SIGNAL_STRENGTH_FOR_CHANNEL(i);
211 session->gpsdata.elevation[i] = SV_ELEVATION_FOR_CHANNEL(i);
212 session->gpsdata.azimuth[i] = SV_AZIMUTH_FOR_CHANNEL(i);
214 if (CHANNEL_USED_IN_SOLUTION(i))
215 session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
217 if(session->gpsdata.PRN[i])
220 /* if the satellite-info setence gives you UTC time, use it */
221 session->gpsdata.skyview_time = NaN;
222 session->gpsdata.satellites_used = nsv;
223 session->gpsdata.satellites_visible = st;
224 gpsd_report(LOG_DATA,
225 "SVINFO: visible=%d used=%d mask={SATELLITE|USED}\n",
226 session->gpsdata.satellites_visible,
227 session->gpsdata.satellites_used);
228 return SATELLITE_IS | USED_IS;
235 _proto__msg_raw(struct gps_device_t *session, unsigned char *buf, size_t data_len)
237 unsigned char i, st, nchan, nsv;
240 if (data_len != RAW_MSG_LEN )
243 gpsd_report(LOG_IO, "_proto_ RAW - raw measurements\n");
244 /* if this protocol has a way to test message validity, use it */
246 if ((flags & _PROTO__SVINFO_VALID) == 0)
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.
256 nchan = GET_NUMBER_OF_CHANNELS();
257 if ((nchan < 1) || (nchan > MAXCHANNELS)) {
258 gpsd_report(LOG_INF, "too many channels reported\n");
262 for (i = 0; i < n; i++){
263 session->gpsdata.PRN[i] = GET_PRN();
264 session->gpsdata.ss[i] = GET_SIGNAL()
265 session->gpsdata.raw.satstat[i] = GET_FLAGS();
266 session->gpsdata.raw.pseudorange[i] = GET_PSEUDORANGE();
267 session->gpsdata.raw.doppler[i] = GET_DOPPLER();
268 session->gpsdata.raw.carrierphase[i] = GET_CARRIER_PHASE();
269 session->gpsdata.raw.mtime[i] = GET_MEASUREMENT_TIME();
270 session->gpsdata.raw.codephase[i] = GET_CODE_PHASE();
271 session->gpsdata.raw.deltarange[i] = GET_DELTA_RANGE();
277 * Parse the data from the device
280 gps_mask_t _proto__dispatch(struct gps_device_t *session, unsigned char *buf, size_t len)
283 int type, used, visible, retmask = 0;
289 * Set this if the driver reliably signals end of cycle.
290 * The core library zeroes it just before it calls each driver's
293 session->cycle_end_reliable = true;
294 if (msgid == MY_START_OF_CYCLE)
296 else if (msgid == MY_END_OF_CYCLE)
297 retmask |= REPORT_IS;
299 type = GET_MESSAGE_TYPE();
301 /* we may need to dump the raw packet */
302 gpsd_report(LOG_RAW, "raw _proto_ packet type 0x%02x length %d: %s\n",
303 type, len, gpsd_hexdump_wrapper(buf, len, LOG_WARN));
306 * The tag field is only 8 bytes; be careful you do not overflow.
307 * Using an abbreviation (eg. "italk" -> "itk") may be useful.
309 (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
310 "_PROTO_%02x", type);
314 /* Deliver message to specific decoder based on message type */
317 /* This gets noisy in a hurry. Change once your driver works */
318 gpsd_report(LOG_WARN, "unknown packet id %d length %d: %s\n",
319 type, len, gpsd_hexdump_wrapper(buf, len, LOG_WARN));
325 /**********************************************************
327 * Externally called routines below here
329 **********************************************************/
331 static bool _proto__probe_detect(struct gps_device_t *session)
334 * This method is used to elicit a positively identifying
335 * response from a candidate device. Some drivers may use
336 * this to test for the presence of a certain kernel module.
340 /* Your testing code here */
347 #ifdef ALLOW_CONTROLSEND
349 * Write data to the device, doing any required padding or checksumming
351 /*@ +charint -usedef -compdef @*/
352 static ssize_t _proto__control_send(struct gps_device_t *session,
353 char *msg, size_t msglen)
357 /* CONSTRUCT THE MESSAGE */
360 * This copy to a public assembly buffer
361 * enables gpsmon to snoop the control message
362 * after it has been sent.
364 session->msgbuflen = msglen;
365 (void)memcpy(session->msgbuf, msg, msglen);
367 /* we may need to dump the message */
368 return gpsd_write(session, session->msgbuf, session->msgbuflen);
369 gpsd_report(LOG_IO, "writing _proto_ control type %02x:%s\n",
370 msg[0], gpsd_hexdump_wrapper(session->msgbuf, session->msgbuflen, LOG_IO));
371 return gpsd_write(session, session->msgbuf, session->msgbuflen);
373 /*@ -charint +usedef +compdef @*/
374 #endif /* ALLOW_CONTROLSEND */
376 #ifdef ALLOW_RECONFIGURE
377 static void _proto__event_hook(struct gps_device_t *session, event_t event)
379 if (event == event_wakeup) {
381 * Code to make the device ready to communicate. This is
382 * run every time we are about to try a different baud
383 * rate in the autobaud sequence. Only needed if the
384 * device is in some kind of sleeping state.
387 if (event == event_identified) {
389 * Fires when the first full packet is recognized from a
390 * previously unidentified device. The session packet counter
391 * is zeroed. If your device has a default cycle time other
392 * than 1 second, set session->device->gpsdata.cycle here. If
393 * possible, get the software version and store it in
397 if (event == event_configure) {
399 * Change sentence mix and set reporting modes as needed.
400 * Called immediately after event_identified fires, then just
401 * after every packet received thereafter, but you probably
402 * only want to take actions on the first few packets after
403 * the session packet counter has been zeroed,
405 * Remember that session->packet.counter is available when you
406 * write this hook; you can use this fact to interleave configuration
407 * sends with the first few packet reads, which is useful for
408 * devices with small receive buffers.
410 } else if (event == event_driver_switch) {
412 * Fires when the driver on a device is changed *after* it
413 * has been identified.
415 } else if (event == event_deactivate) {
417 * Fires when the device is deactivated. Usr this to revert
418 * whatever was done at event_identify and event_configure
421 } else if (event == event_reactivate) {
423 * Fires when a device is reactivated after having been closed.
424 * Use this hook for re-establishing device settings that
425 * it doesn't hold through closes.
431 * This is the entry point to the driver. When the packet sniffer recognizes
432 * a packet for this driver, it calls this method which passes the packet to
433 * the binary processor or the nmea processor, depending on the session type.
435 static gps_mask_t _proto__parse_input(struct gps_device_t *session)
439 if (session->packet.type == _PROTO__PACKET) {
440 st = _proto__dispatch(session, session->packet.outbuffer, session->packet.outbuflen);
441 session->gpsdata.driver_mode = MODE_BINARY;
444 } else if (session->packet.type == NMEA_PACKET) {
445 st = nmea_parse((char *)session->packet.outbuffer, session);
446 session->gpsdata.driver_mode = MODE_NMEA;
448 #endif /* NMEA_ENABLE */
453 static bool _proto__set_speed(struct gps_device_t *session,
454 speed_t speed, char parity, int stopbits)
457 * Set port operating mode, speed, parity, stopbits etc. here.
458 * Note: parity is passed as 'N'/'E'/'O', but you should program
459 * defensively and allow 0/1/2 as well.
464 * Switch between NMEA and binary mode, if supported
466 static void _proto__set_mode(struct gps_device_t *session, int mode)
468 if (mode == MODE_NMEA) {
469 // _proto__to_nmea(session->gpsdata.gps_fd,session->gpsdata.baudrate); /* send the mode switch control string */
470 session->gpsdata.driver_mode = MODE_NMEA;
472 * Anticipatory switching works only when the packet getter is the
473 * generic one and it recognizes packets of the type this driver
474 * is expecting. This should be the normal case.
476 (void)gpsd_switch_driver(session, "Generic NMEA");
478 session->back_to_nmea = false;
479 session->gpsdata.driver_mode = MODE_BINARY;
482 #endif /* ALLOW_RECONFIGURE */
485 static double _proto_ntp_offset(struct gps_device_t *session)
488 * If NTP notification is enabled, the GPS will occasionally NTP
489 * its notion of the time. This will lag behind actual time by
490 * some amount which has to be determined by observation vs. (say
491 * WWVB radio broadcasts) and, furthermore, may differ by baud
492 * rate. This method is for computing the NTP fudge factor. If
493 * it's absent, an offset of 0.0 will be assumed, effectively
494 * falling back on what's in ntp.conf. When it returns NAN,
495 * nothing will be sent to NTP.
497 return MAGIC_CONSTANT;
499 #endif /* NTPSHM_ENABLE */
501 static void _proto__wrapup(struct gps_device_t *session)
505 /* The methods in this code take parameters and have */
506 /* return values that conform to the requirements AT */
507 /* THE TIME THE CODE WAS WRITTEN. */
509 /* These values may well have changed by the time */
510 /* you read this and methods could have been added */
511 /* or deleted. Unused methods can be set to NULL. */
513 /* The latest version can be found by inspecting */
514 /* the contents of struct gps_type_t in gpsd.h. */
516 /* This always contains the correct definitions that */
517 /* any driver must use to compile. */
519 /* This is everything we export */
521 const struct gps_type_t _proto__binary = {
522 /* Full name of type */
523 .type_name = "_proto_ binary",
524 /* Associated lexer packet type */
525 .packet_type = _PROTO__PACKET,
526 /* Response string that identifies device (not active) */
528 /* Number of satellite channels supported by the device */
530 /* Startup-time device detector */
531 .probe_detect = _proto__probe_detect,
532 /* Packet getter (using default routine) */
533 .get_packet = generic_get,
534 /* Parse message packets */
535 .parse_packet = _proto__parse_input,
536 /* RTCM handler (using default routine) */
537 .rtcm_writer = pass_rtcm,
538 /* fire on various lifetime events */
539 .event_hook = _proto__event_hook,
540 #ifdef ALLOW_RECONFIGURE
541 /* Speed (baudrate) switch */
542 .speed_switcher = _proto__set_speed,
543 /* Switch to NMEA mode */
544 .mode_switcher = _proto__set_mode,
545 /* Message delivery rate switcher (not active) */
546 .rate_switcher = NULL,
547 /* Minimum cycle time of the device */
549 #endif /* ALLOW_RECONFIGURE */
550 #ifdef ALLOW_CONTROLSEND
551 /* Control string sender - should provide checksum and headers/trailer */
552 .control_send = _proto__control_send,
553 #endif /* ALLOW_CONTROLSEND */
555 .ntp_offset = _proto_ntp_offset,
556 #endif /* NTPSHM_ENABLE */
559 #endif /* defined(_PROTO__ENABLE) && defined(BINARY_ENABLE) */