cleanup specfile for packaging
[profile/ivi/gpsd.git] / driver_proto.c
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.
16  *
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
21  * driver functions.
22  *
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:
26  * Makefile.am
27  * drivers.c
28  * gpsd.h-tail
29  * libgpsd_core.c
30  * packet.c
31  * packet_states.h
32  *
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.
35  */
36
37 #include <sys/types.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <math.h>
42 #include <ctype.h>
43 #ifndef S_SPLINT_S
44 #include <unistd.h>
45 #endif /* S_SPLINT_S */
46 #include <time.h>
47 #include <stdio.h>
48
49 #include "gpsd.h"
50 #if defined(_PROTO__ENABLE) && defined(BINARY_ENABLE)
51
52 #include "bits.h"
53
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 );
60
61 /*
62  * These methods may be called elsewhere in gpsd
63  */
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);
69
70 /*
71  * Decode the navigation solution message
72  */
73 static gps_mask_t
74 _proto__msg_navsol(struct gps_device_t *session, unsigned char *buf, size_t data_len)
75 {
76     gps_mask_t mask;
77     int flags;
78     double Px, Py, Pz, Vx, Vy, Vz;
79
80     if (data_len != _PROTO__NAVSOL_MSG_LEN)
81         return 0;
82
83     gpsd_report(LOG_IO, "_proto_ NAVSOL - navigation data\n");
84     /* if this protocol has a way to test message validity, use it */
85     flags = GET_FLAGS();
86     if ((flags & _PROTO__SOLUTION_VALID) == 0)
87         return 0;
88
89     mask = ONLINE_IS;
90
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  ;
97
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();
102     /*
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
109      * that increase DOP.
110      */
111     session->gpsdata.dop.hdop = GET_HDOP();
112     session->gpsdata.dop.vdop = GET_VDOP();
113     /* other DOP if available */
114     mask |= DOP_IS;
115
116     session->newdata.mode = GET_FIX_MODE();
117     session->gpsdata.status = GET_FIX_STATUS();
118
119     /*
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.
123      */
124     mask |= MODE_IS | STATUS_IS | REPORT_IS;
125
126     /* 
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.
130      */
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));
139
140     return mask;
141 }
142
143 /**
144  * GPS Leap Seconds
145  */
146 static gps_mask_t
147 _proto__msg_utctime(struct gps_device_t *session, unsigned char *buf, size_t data_len)
148 {
149     double t;
150
151     if (data_len != UTCTIME_MSG_LEN)
152         return 0;
153
154     gpsd_report(LOG_IO, "_proto_ UTCTIME - navigation data\n");
155     /* if this protocol has a way to test message validity, use it */
156     flags = GET_FLAGS();
157     if ((flags & _PROTO__TIME_VALID) == 0)
158         return 0;
159
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;
165
166     t = gpstime_to_unix(gps_week, session->context->gps_tow)
167         - session->context->leap_seconds;
168     session->newdata.time = t;
169
170     return TIME_IS | ONLINE_IS;
171 }
172
173 /**
174  * GPS Satellite Info
175  */
176 static gps_mask_t
177 _proto__msg_svinfo(struct gps_device_t *session, unsigned char *buf, size_t data_len)
178 {
179     unsigned char i, st, nchan, nsv;
180     unsigned int tow;
181
182     if (data_len != SVINFO_MSG_LEN )
183         return 0;
184
185     gpsd_report(LOG_IO, "_proto_ SVINFO - navigation data\n");
186     /* if this protocol has a way to test message validity, use it */
187     flags = GET_FLAGS();
188     if ((flags & _PROTO__SVINFO_VALID) == 0)
189         return 0;
190
191     /*
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.
197      */
198     nchan = GET_NUMBER_OF_CHANNELS();
199     if ((nchan < 1) || (nchan > MAXCHANNELS)) {
200         gpsd_report(LOG_INF, "too many channels reported\n");
201         return 0;
202     }
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);
208
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);
213
214         if (CHANNEL_USED_IN_SOLUTION(i))
215             session->gpsdata.used[nsv++] = session->gpsdata.PRN[i];
216
217         if(session->gpsdata.PRN[i])
218                 st++;
219     }
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;
229 }
230
231 /**
232  * Raw measurements
233  */
234 static gps_mask_t
235 _proto__msg_raw(struct gps_device_t *session, unsigned char *buf, size_t data_len)
236 {
237     unsigned char i, st, nchan, nsv;
238     unsigned int tow;
239
240     if (data_len != RAW_MSG_LEN )
241         return 0;
242
243     gpsd_report(LOG_IO, "_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_report(LOG_INF, "too many channels reported\n");
259         return 0;
260     }
261
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();
272     }
273     return RAW_IS;
274 }
275
276 /**
277  * Parse the data from the device
278  */
279 /*@ +charint @*/
280 gps_mask_t _proto__dispatch(struct gps_device_t *session, unsigned char *buf, size_t len)
281 {
282     size_t i;
283     int type, used, visible, retmask = 0;
284
285     if (len == 0)
286         return 0;
287
288     /*
289      * Set this if the driver reliably signals end of cycle.
290      * The core library zeroes it just before it calls each driver's
291      * packet analyzer.
292      */
293     session->cycle_end_reliable = true;
294     if (msgid == MY_START_OF_CYCLE)
295         retmask |= CLEAR_IS;
296     else if (msgid == MY_END_OF_CYCLE)
297         retmask |= REPORT_IS;
298
299     type = GET_MESSAGE_TYPE();
300
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));
304
305    /*
306     * The tag field is only 8 bytes; be careful you do not overflow.
307     * Using an abbreviation (eg. "italk" -> "itk") may be useful.
308     */
309     (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
310         "_PROTO_%02x", type);
311
312     switch (type)
313     {
314         /* Deliver message to specific decoder based on message type */
315
316     default:
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));
320         return 0;
321     }
322 }
323 /*@ -charint @*/
324
325 /**********************************************************
326  *
327  * Externally called routines below here
328  *
329  **********************************************************/
330
331 static bool _proto__probe_detect(struct gps_device_t *session)
332 {
333    /*
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.
337     */
338    int test, satisfied;
339
340    /* Your testing code here */
341    test=satisfied=0;
342    if (test==satisfied)
343       return true;
344    return false;
345 }
346
347 #ifdef ALLOW_CONTROLSEND
348 /**
349  * Write data to the device, doing any required padding or checksumming
350  */
351 /*@ +charint -usedef -compdef @*/
352 static ssize_t _proto__control_send(struct gps_device_t *session,
353                            char *msg, size_t msglen)
354 {
355    bool ok;
356
357    /* CONSTRUCT THE MESSAGE */
358
359    /* 
360     * This copy to a public assembly buffer 
361     * enables gpsmon to snoop the control message
362     * after it has been sent.
363     */
364    session->msgbuflen = msglen;
365    (void)memcpy(session->msgbuf, msg, msglen);
366
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);
372 }
373 /*@ -charint +usedef +compdef @*/
374 #endif /* ALLOW_CONTROLSEND */
375
376 #ifdef ALLOW_RECONFIGURE
377 static void _proto__event_hook(struct gps_device_t *session, event_t event)
378 {
379     if (event == event_wakeup) {
380        /*
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.
385         */
386     }
387     if (event == event_identified) {
388         /*
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
394          * session->subtype.
395          */
396     }
397     if (event == event_configure) {
398         /* 
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,
404          *
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.
409          */
410     } else if (event == event_driver_switch) {
411         /*
412          * Fires when the driver on a device is changed *after* it
413          * has been identified.
414          */
415     } else if (event == event_deactivate) {
416         /*
417          * Fires when the device is deactivated.  Usr this to revert
418          * whatever was done at event_identify and event_configure
419          * time.
420          */
421     } else if (event == event_reactivate) {
422        /*
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.
426         */
427     }
428 }
429
430 /*
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.
434  */
435 static gps_mask_t _proto__parse_input(struct gps_device_t *session)
436 {
437     gps_mask_t st;
438
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;
442         return st;
443 #ifdef NMEA_ENABLE
444     } else if (session->packet.type == NMEA_PACKET) {
445         st = nmea_parse((char *)session->packet.outbuffer, session);
446         session->gpsdata.driver_mode = MODE_NMEA;
447         return st;
448 #endif /* NMEA_ENABLE */
449     } else
450         return 0;
451 }
452
453 static bool _proto__set_speed(struct gps_device_t *session,
454                               speed_t speed, char parity, int stopbits)
455 {
456     /* 
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.
460      */
461 }
462
463 /*
464  * Switch between NMEA and binary mode, if supported
465  */
466 static void _proto__set_mode(struct gps_device_t *session, int mode)
467 {
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;
471         /* 
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.
475          */
476         (void)gpsd_switch_driver(session, "Generic NMEA");
477     } else {
478         session->back_to_nmea = false;
479         session->gpsdata.driver_mode = MODE_BINARY;
480     }
481 }
482 #endif /* ALLOW_RECONFIGURE */
483
484 #ifdef NTPSHM_ENABLE
485 static double _proto_ntp_offset(struct gps_device_t *session)
486 {
487     /*
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.
496      */
497     return MAGIC_CONSTANT;
498 }
499 #endif /* NTPSHM_ENABLE */
500
501 static void _proto__wrapup(struct gps_device_t *session)
502 {
503 }
504
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.                    */
508 /*                                                   */
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.    */
512 /*                                                   */
513 /* The latest version can be found by inspecting   */
514 /* the contents of struct gps_type_t in gpsd.h.      */
515 /*                                                   */
516 /* This always contains the correct definitions that */
517 /* any driver must use to compile.                   */
518
519 /* This is everything we export */
520 /* *INDENT-OFF* */
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) */
527     .trigger          = NULL,
528     /* Number of satellite channels supported by the device */
529     .channels         = 12,
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 */
548     .min_cycle        = 1,
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 */
554 #ifdef NTPSHM_ENABLE
555     .ntp_offset     = _proto_ntp_offset,
556 #endif /* NTPSHM_ENABLE */
557 /* *INDENT-ON* */
558 };
559 #endif /* defined(_PROTO__ENABLE) && defined(BINARY_ENABLE) */
560