cleanup specfile for packaging
[profile/ivi/gpsd.git] / gpxlogger.c
1 /*
2  * This file is Copyright (c) 2010 by the GPSD project
3  * BSD terms apply: see the file COPYING in the distribution root for details.
4  */
5 #include <stdlib.h>
6 #include "gpsd_config.h"
7 #include <sys/types.h>
8 #include <string.h>
9 #include <stdio.h>
10 #ifdef HAVE_SYSLOG_H
11 #include <syslog.h>
12 #endif /* HAVE_SYSLOG_H */
13 #include <math.h>
14 #include <time.h>
15 #include <signal.h>
16 #include <getopt.h>
17 #include <errno.h>
18 #ifndef S_SPLINT_S
19 #include <unistd.h>
20 #endif /* S_SPLINT_S */
21 #include "gps.h"
22 #include "gpsdclient.h"
23 #include "revision.h"
24
25 #ifdef S_SPLINT_S
26 extern struct tm *gmtime_r(const time_t *, /*@out@*/ struct tm *tp);
27 #endif /* S_SPLINT_S */
28
29 /**************************************************************************
30  *
31  * Transport-layer-independent functions
32  *
33  **************************************************************************/
34
35 static char *author = "Amaury Jacquot, Chris Kuethe";
36 static char *license = "BSD";
37 static char *progname;
38
39 static time_t int_time, old_int_time;
40 static bool intrack = false;
41 static bool first = true;
42 static time_t timeout = 5;      /* seconds */
43 #ifdef CLIENTDEBUG_ENABLE
44 static int debug;
45 #endif /* CLIENTDEBUG_ENABLE */
46
47 static void print_gpx_header(void)
48 {
49     (void)printf("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
50     (void)printf("<gpx version=\"1.1\" creator=\"navsys logger\"\n");
51     (void)
52         printf
53         ("        xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
54     (void)printf("        xmlns=\"http://www.topografix.com/GPX/1.1\"\n");
55     (void)
56         printf
57         ("        xsi:schemaLocation=\"http://www.topografix.com/GPS/1/1\n");
58     (void)printf("        http://www.topografix.com/GPX/1/1/gpx.xsd\">\n");
59     (void)printf(" <metadata>\n");
60     (void)printf("  <name>NavSys GPS logger dump</name>\n");
61     (void)printf("  <author>%s</author>\n", author);
62     (void)printf("  <copyright>%s</copyright>\n", license);
63     (void)printf(" </metadata>\n");
64     (void)fflush(stdout);
65 }
66
67 static void print_gpx_trk_end(void)
68 {
69     (void)printf("  </trkseg>\n");
70     (void)printf(" </trk>\n");
71     (void)fflush(stdout);
72 }
73
74 static void print_gpx_footer(void)
75 {
76     if (intrack)
77         print_gpx_trk_end();
78     (void)printf("</gpx>\n");
79     (void)fclose(stdout);
80 }
81
82 static void print_gpx_trk_start(void)
83 {
84     (void)printf(" <trk>\n");
85     (void)printf("  <trkseg>\n");
86     (void)fflush(stdout);
87 }
88
89 static void print_fix(struct gps_fix_t *fix, struct tm *time)
90 {
91     (void)printf("   <trkpt lat=\"%f\" lon=\"%f\">\n",
92                  fix->latitude, fix->longitude);
93     (void)printf("    <ele>%f</ele>\n", fix->altitude);
94     (void)printf("    <time>%04d-%02d-%02dT%02d:%02d:%02dZ</time>\n",
95                  time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
96                  time->tm_hour, time->tm_min, time->tm_sec);
97     if (fix->mode == MODE_NO_FIX)
98         (void)fprintf(stdout, "    <fix>none</fix>\n");
99     else
100         (void)fprintf(stdout, "    <fix>%dd</fix>\n", fix->mode);
101 #if 0
102     /*
103      * Can't print this more detailed report because in D-Bus mode
104      * we don't necessarily have access to some of the stuff in gsdata.
105      * Might mean some of this stuff should be promoted.
106      */
107     if ((gpsdata->status >= 2) && (gpsdata->fix.mode >= MODE_3D)) {
108         /* dgps or pps */
109         if (gpsdata->fix.mode == 4) {   /* military pps */
110             (void)printf("        <fix>pps</fix>\n");
111         } else {                /* civilian dgps or sbas */
112             (void)printf("        <fix>dgps</fix>\n");
113         }
114     } else {                    /* no dgps or pps */
115         if (gpsdata->fix.mode == MODE_3D) {
116             (void)printf("        <fix>3d</fix>\n");
117         } else if (gpsdata->fix.mode == MODE_2D) {
118             (void)printf("        <fix>2d</fix>\n");
119         } else if (gpsdata->fix.mode == MODE_NOFIX) {
120             (void)printf("        <fix>none</fix>\n");
121         }                       /* don't print anything if no fix indicator */
122     }
123
124     /* print # satellites used in fix, if reasonable to do so */
125     if (gpsdata->fix.mode >= MODE_2D) {
126         (void)printf("        <hdop>%.1f</hdop>\n", gpsdata->hdop);
127         (void)printf("        <sat>%d</sat>\n", gpsdata->satellites_used);
128     }
129 #endif
130
131     (void)printf("   </trkpt>\n");
132     (void)fflush(stdout);
133 }
134
135 static void conditionally_log_fix(struct gps_fix_t *gpsfix)
136 {
137     int_time = (time_t) floor(gpsfix->time);
138     if ((int_time != old_int_time) && gpsfix->mode >= MODE_2D) {
139         struct tm time;
140         /* 
141          * Make new track if the jump in time is above
142          * timeout.  Handle jumps both forward and
143          * backwards in time.  The clock sometimes jumps
144          * backward when gpsd is submitting junk on the
145          * dbus.
146          */
147         /*@-type@*/
148         if (fabs(int_time - old_int_time) > timeout && !first) {
149             print_gpx_trk_end();
150             intrack = false;
151         }
152         /*@+type@*/
153
154         if (!intrack) {
155             print_gpx_trk_start();
156             intrack = true;
157             if (first)
158                 first = false;
159         }
160
161         old_int_time = int_time;
162         (void)gmtime_r(&(int_time), &time);
163         print_fix(gpsfix, &time);
164     }
165 }
166
167 static void quit_handler(int signum)
168 {
169     /* don't clutter the logs on Ctrl-C */
170     if (signum != SIGINT)
171         syslog(LOG_INFO, "exiting, signal %d received", signum);
172     print_gpx_footer();
173     exit(0);
174 }
175
176 static struct gps_fix_t gpsfix;
177
178 #ifdef DBUS_ENABLE
179 /**************************************************************************
180  *
181  * Doing it with D-Bus
182  *
183  **************************************************************************/
184
185 #include <glib.h>
186 #include <dbus/dbus.h>
187 #include <dbus/dbus-glib-lowlevel.h>
188 #include <dbus/dbus-glib.h>
189
190 #include <glib/gprintf.h>
191
192 #define EMIX(x, y)      (((x) > (y)) ? (x) : (y))
193
194 DBusConnection *connection;
195
196 static char gpsd_devname[BUFSIZ];
197
198 static DBusHandlerResult handle_gps_fix(DBusMessage * message)
199 {
200     DBusError error;
201     /* this packet format was designed before we split eph */
202     double eph = EMIX(gpsfix.epx, gpsfix.epy);
203
204     dbus_error_init(&error);
205
206     dbus_message_get_args(message,
207                           &error,
208                           DBUS_TYPE_DOUBLE, &gpsfix.time,
209                           DBUS_TYPE_INT32, &gpsfix.mode,
210                           DBUS_TYPE_DOUBLE, &gpsfix.ept,
211                           DBUS_TYPE_DOUBLE, &gpsfix.latitude,
212                           DBUS_TYPE_DOUBLE, &gpsfix.longitude,
213                           DBUS_TYPE_DOUBLE, &eph,
214                           DBUS_TYPE_DOUBLE, &gpsfix.altitude,
215                           DBUS_TYPE_DOUBLE, &gpsfix.epv,
216                           DBUS_TYPE_DOUBLE, &gpsfix.track,
217                           DBUS_TYPE_DOUBLE, &gpsfix.epd,
218                           DBUS_TYPE_DOUBLE, &gpsfix.speed,
219                           DBUS_TYPE_DOUBLE, &gpsfix.eps,
220                           DBUS_TYPE_DOUBLE, &gpsfix.climb,
221                           DBUS_TYPE_DOUBLE, &gpsfix.epc,
222                           DBUS_TYPE_STRING, &gpsd_devname, DBUS_TYPE_INVALID);
223
224     conditionally_log_fix(&gpsfix);
225     return DBUS_HANDLER_RESULT_HANDLED;
226 }
227
228 /*
229  * Message dispatching function
230  *
231  */
232 static DBusHandlerResult signal_handler(DBusConnection * connection,
233                                         DBusMessage * message)
234 {
235     /* dummy, need to use the variable for some reason */
236     connection = NULL;
237
238     if (dbus_message_is_signal(message, "org.gpsd", "fix"))
239         return handle_gps_fix(message);
240     /*
241      * ignore all other messages
242      */
243
244     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
245 }
246
247 static int dbus_mainloop(void)
248 {
249     GMainLoop *mainloop;
250     DBusError error;
251
252     mainloop = g_main_loop_new(NULL, FALSE);
253
254     dbus_error_init(&error);
255     connection = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
256     if (dbus_error_is_set(&error)) {
257         syslog(LOG_CRIT, "%s: %s", error.name, error.message);
258         return 3;
259     }
260
261     dbus_bus_add_match(connection, "type='signal'", &error);
262     if (dbus_error_is_set(&error)) {
263         syslog(LOG_CRIT, "unable to add match for signals %s: %s", error.name,
264                error.message);
265         return 4;
266     }
267
268     if (!dbus_connection_add_filter
269         (connection, (DBusHandleMessageFunction) signal_handler, NULL,
270          NULL)) {
271         syslog(LOG_CRIT, "unable to register filter with the connection");
272         return 5;
273     }
274
275     dbus_connection_setup_with_g_main(connection, NULL);
276
277     g_main_loop_run(mainloop);
278     return 0;
279 }
280
281 #endif /* DBUS_ENABLE */
282
283 /**************************************************************************
284  *
285  * Doing it with sockets
286  *
287  **************************************************************************/
288
289 static struct fixsource_t source;
290
291 static void process(struct gps_data_t *gpsdata,
292                     char *buf UNUSED, size_t len UNUSED)
293 {
294     /* this is where we implement source-device filtering */
295     if (gpsdata->dev.path[0] != '\0' && source.device != NULL
296         && strcmp(source.device, gpsdata->dev.path) != 0)
297         return;
298
299     conditionally_log_fix(&gpsdata->fix);
300 }
301
302 /*@-mustfreefresh -compdestroy@*/
303 static int socket_mainloop(void)
304 {
305     fd_set fds;
306     struct gps_data_t gpsdata;
307
308     if (gps_open_r(source.server, source.port, &gpsdata) != 0) {
309         (void)fprintf(stderr,
310                       "%s: no gpsd running or network error: %d, %s\n",
311                       progname, errno, gps_errstr(errno));
312         exit(1);
313     }
314
315     gps_set_raw_hook(&gpsdata, process);
316     (void)gps_stream(&gpsdata, WATCH_ENABLE, NULL);
317
318     for (;;) {
319         int data;
320         struct timeval tv;
321
322         FD_ZERO(&fds);
323         FD_SET(gpsdata.gps_fd, &fds);
324
325         tv.tv_usec = 250000;
326         tv.tv_sec = 0;
327         data = select(gpsdata.gps_fd + 1, &fds, NULL, NULL, &tv);
328
329         if (data == -1) {
330             (void)fprintf(stderr, "%s\n", strerror(errno));
331             break;
332         } else if (data)
333             (void)gps_read(&gpsdata);
334     }
335     (void)gps_close(&gpsdata);
336     return 0;
337 }
338 /*@+mustfreefresh +compdestroy@*/
339
340 /**************************************************************************
341  *
342  * Main sequence
343  *
344  **************************************************************************/
345
346 static void usage(void)
347 {
348     fprintf(stderr,
349             "Usage: %s [-V] [-h] [-i timeout] [-j casoc] [server[:port:[device]]]\n",
350             progname);
351     fprintf(stderr,
352             "\tdefaults to '%s -i 5 -j 0 localhost:2947'\n", progname);
353     exit(1);
354 }
355
356 int main(int argc, char **argv)
357 {
358     int ch;
359
360     progname = argv[0];
361     while ((ch = getopt(argc, argv, "D:hi:V")) != -1) {
362         switch (ch) {
363 #ifdef CLIENTDEBUG_ENABLE
364         case 'D':
365             debug = atoi(optarg);
366             gps_enable_debug(debug, stdout);
367             break;
368 #endif /* CLIENTDEBUG_ENABLE */
369         case 'i':               /* set polling interfal */
370             timeout = (time_t) atoi(optarg);
371             if (timeout < 1)
372                 timeout = 1;
373             if (timeout >= 3600)
374                 fprintf(stderr,
375                         "WARNING: track timeout is an hour or more!\n");
376             break;
377         case 'V':
378             (void)fprintf(stderr, "gpxlogger revision " REVISION "\n");
379             exit(0);
380         default:
381             usage();
382             /* NOTREACHED */
383         }
384     }
385
386     if (optind < argc) {
387         gpsd_source_spec(argv[optind], &source);
388     } else
389         gpsd_source_spec(NULL, &source);
390 #if 0
391     (void)printf("<!-- server: %s port: %s  device: %s -->\n",
392                  source.server, source.port, source.device);
393 #endif
394
395     /* initializes the gpsfix data structure */
396     gps_clear_fix(&gpsfix);
397
398     /* catch all interesting signals */
399     (void)signal(SIGTERM, quit_handler);
400     (void)signal(SIGQUIT, quit_handler);
401     (void)signal(SIGINT, quit_handler);
402
403     //openlog ("gpxlogger", LOG_PID | LOG_NDELAY , LOG_DAEMON);
404     //syslog (LOG_INFO, "---------- STARTED ----------");
405
406     print_gpx_header();
407
408 #ifdef DBUS_ENABLE
409     /* To force socket use in the default way just give a 'localhost' arg */
410     if (optind < argc)
411         return socket_mainloop();
412     else
413         return dbus_mainloop();
414 #else
415     return socket_mainloop();
416 #endif
417 }