Cleanup
[fso-gpsd.git] / fso-gpsd.c
blob0d1867723ee0c8e1e8fa6884b8b69f451df9bcf4
1 /* fso-gpsd - a gpsd compatibility daemon for the FSO frameworkd
3 * Copyright (C) 2008 Sascha Wessel <wessel@nefkom.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <unistd.h>
23 #include <syslog.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <netdb.h>
28 #include <math.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <dbus/dbus-glib.h>
34 #include <glib-object.h>
35 #include <glib.h>
37 #define KNOTS_TO_KPH (1.852)
38 #define KNOTS_TO_MPS (0.51444444)
40 /******************************************************************************/
42 #define NAME "fso-gpsd"
43 #define PORT "2947"
44 #define OUSAGED "org.freesmartphone.ousaged"
45 #define OGPSD "org.freesmartphone.ogpsd"
46 #define USAGE "/org/freesmartphone/Usage"
47 #define GYPSY "/org/freedesktop/Gypsy"
49 /******************************************************************************/
51 #define ERROR(...) \
52 do { \
53 g_log(NULL, G_LOG_LEVEL_ERROR, __VA_ARGS__); \
54 abort(); \
55 } while (0)
57 #define CRITICAL(...) \
58 do { \
59 if (log_level & G_LOG_LEVEL_CRITICAL) { \
60 g_log(NULL, G_LOG_LEVEL_CRITICAL, __VA_ARGS__); \
61 } \
62 } while (0)
64 #define WARNING(...) \
65 do { \
66 if (log_level & G_LOG_LEVEL_WARNING) { \
67 g_log(NULL, G_LOG_LEVEL_WARNING, __VA_ARGS__); \
68 } \
69 } while (0)
71 #define MESSAGE(...) \
72 do { \
73 if (log_level & G_LOG_LEVEL_MESSAGE) { \
74 g_log(NULL, G_LOG_LEVEL_MESSAGE, __VA_ARGS__); \
75 } \
76 } while (0)
78 #define INFO(...) \
79 do { \
80 if (log_level & G_LOG_LEVEL_INFO) { \
81 g_log(NULL, G_LOG_LEVEL_INFO, __VA_ARGS__); \
82 } \
83 } while (0)
85 #define DEBUG(...) \
86 do { \
87 if (log_level & G_LOG_LEVEL_DEBUG) { \
88 g_log(NULL, G_LOG_LEVEL_DEBUG, __VA_ARGS__); \
89 } \
90 } while (0)
92 /******************************************************************************/
94 #define POSITION_LATITUDE_IS_VALID() (position_fields & (1 << 0))
95 #define POSITION_LONGITUDE_IS_VALID() (position_fields & (1 << 1))
96 #define POSITION_ALTITUDE_IS_VALID() (position_fields & (1 << 2))
98 #define ACCURACY_PDOP_IS_VALID() (accuracy_fields & (1 << 0))
99 #define ACCURACY_HDOP_IS_VALID() (accuracy_fields & (1 << 1))
100 #define ACCURACY_VDOP_IS_VALID() (accuracy_fields & (1 << 2))
102 #define COURSE_SPEED_IS_VALID() (course_fields & (1 << 0))
103 #define COURSE_DIRECTION_IS_VALID() (course_fields & (1 << 1))
104 #define COURSE_CLIMB_IS_VALID() (course_fields & (1 << 2))
106 /******************************************************************************/
108 struct client {
109 GIOChannel *channel;
110 gboolean watcher;
111 gboolean raw;
114 struct satellite {
115 unsigned prn;
116 gboolean used;
117 unsigned elevation;
118 unsigned azimuth;
119 unsigned snr;
122 /******************************************************************************/
124 static int log_level = G_LOG_LEVEL_ERROR
125 | G_LOG_LEVEL_CRITICAL
126 | G_LOG_LEVEL_WARNING;
128 static gboolean background = TRUE;
129 static gboolean nowait = FALSE;
131 /******************************************************************************/
133 static gboolean info_queued = FALSE;
134 static gboolean sats_queued = FALSE;
135 static gboolean nmea_queued = FALSE;
137 /******************************************************************************/
139 static GList *client_list = NULL;
141 /******************************************************************************/
143 static DBusGConnection *connection;
144 static DBusGProxy *proxy_usage;
145 static DBusGProxy *proxy_device;
146 static DBusGProxy *proxy_position;
147 static DBusGProxy *proxy_accuracy;
148 static DBusGProxy *proxy_course;
149 static DBusGProxy *proxy_satellite;
150 static DBusGProxy *proxy_time;
152 /******************************************************************************/
154 static gboolean device_connectionstatus;
155 static int device_fixstatus; /* 0=INVALID, 1=NONE, 2=2D, 3=3D */
157 static int position_fields;
158 static int position_timestamp;
159 static double position_latitude;
160 static double position_longitude;
161 static double position_altitude;
163 static int accuracy_fields;
164 static double accuracy_pdop;
165 static double accuracy_hdop;
166 static double accuracy_vdop;
168 static int course_fields;
169 static int course_timestamp;
170 static double course_speed;
171 static double course_direction;
172 static double course_climb;
174 #define SATELLITEC_MAX 32
175 static struct satellite satellite_satellitev[SATELLITEC_MAX];
176 static unsigned satellite_satellitec;
178 static int time_seconds;
180 /******************************************************************************/
182 static GString *nmea_gga;
183 static GString *nmea_gsa;
184 static GString *nmea_gsv[3];
185 static GString *nmea_rmc;
186 static GString *nmea_gll;
187 static GString *nmea_vtg;
189 /******************************************************************************/
191 static int
192 get_fixstatus(void)
194 if (POSITION_LATITUDE_IS_VALID() && POSITION_LONGITUDE_IS_VALID()) {
195 if (POSITION_ALTITUDE_IS_VALID()) {
196 return MIN(device_fixstatus, 3);
197 } else {
198 return MIN(device_fixstatus, 2);
200 } else {
201 return MIN(device_fixstatus, 1);
205 static gboolean
206 check_period(const GTimeVal *tv1, const GTimeVal *tv2, glong sec)
208 glong tv1_sec = tv1->tv_sec + sec;
209 glong tv1_usec = tv1->tv_usec;
210 glong tv2_sec = tv2->tv_sec;
211 glong tv2_usec = tv2->tv_usec;
213 if (tv1_sec == tv2_sec) {
214 return (tv1_usec > tv2_usec);
215 } else {
216 return (tv1_sec > tv2_sec);
220 /******************************************************************************/
222 static void
223 shutdown_client(struct client *client)
225 if (client->channel) {
226 MESSAGE("Shutdown client");
228 g_io_channel_shutdown(client->channel, FALSE, NULL);
231 client->channel = NULL;
232 client->watcher = FALSE;
233 client->raw = FALSE;
236 static void
237 send_to_client(struct client *client, const char *string, gsize len)
239 GIOStatus status;
240 gsize bytes_written;
242 if (!client->channel) {
243 return;
246 status = g_io_channel_write_chars(client->channel, string,
247 len, &bytes_written, NULL);
249 if ((bytes_written != len) || (status != G_IO_STATUS_NORMAL)) {
250 shutdown_client(client);
254 /******************************************************************************/
256 static void
257 add_nmea_checksum(GString *nmea)
259 int checksum = 0;
260 char *p;
262 p = nmea->str;
264 if (*p == '$') {
265 p++;
268 while (*p) {
269 checksum ^= (int) *p;
270 p++;
273 g_string_append_printf(nmea, "*%02x", checksum);
276 static void
277 add_nmea_time(GString *nmea)
279 if (time_seconds <= 0) {
280 g_string_append_c(nmea, ',');
281 } else {
282 struct tm *tm;
283 time_t seconds;
285 seconds = time_seconds;
286 tm = gmtime(&seconds);
288 g_string_append_printf(nmea, ",%02d%02d%02d.00",
289 tm->tm_hour, tm->tm_min, tm->tm_sec);
293 static void
294 add_nmea_latitude(GString *nmea)
296 if (!POSITION_LATITUDE_IS_VALID()) {
297 g_string_append_c(nmea, ',');
298 g_string_append_c(nmea, ',');
299 } else {
300 double a, d;
302 a = fabs(position_latitude);
303 d = floor(a);
305 g_string_append_printf(nmea, ",%010.5f,%c",
306 (d * 100.0) + ((a - d) * 60.0),
307 (position_latitude > 0) ? 'N' : 'S');
311 static void
312 add_nmea_longitude(GString *nmea)
314 if (!POSITION_LONGITUDE_IS_VALID()) {
315 g_string_append_c(nmea, ',');
316 g_string_append_c(nmea, ',');
317 } else {
318 double a, d;
320 a = fabs(position_longitude);
321 d = floor(a);
323 g_string_append_printf(nmea, ",%011.5f,%c",
324 (d * 100.0) + ((a - d) * 60.0),
325 (position_longitude > 0) ? 'E' : 'W');
329 /******************************************************************************/
331 static void
332 update_nmea_gga(void)
334 unsigned i, used;
336 g_string_assign(nmea_gga, "$GPGGA");
338 add_nmea_time(nmea_gga);
340 add_nmea_latitude(nmea_gga);
342 add_nmea_longitude(nmea_gga);
344 g_string_append_printf(nmea_gga, ",%1d", MAX(get_fixstatus() - 1, 0));
346 for (i = 0, used = 0; i < satellite_satellitec; i++) {
347 if (satellite_satellitev[i].used) {
348 used++;
352 g_string_append_printf(nmea_gga, ",%02d", used);
354 if (!ACCURACY_HDOP_IS_VALID()) {
355 g_string_append_c(nmea_gga, ',');
356 } else {
357 g_string_append_printf(nmea_gga, ",%04.2f", accuracy_hdop);
360 if (!POSITION_ALTITUDE_IS_VALID()) {
361 g_string_append_c(nmea_gga, ',');
362 g_string_append_c(nmea_gga, ',');
363 } else {
364 g_string_append_printf(nmea_gga, ",%03.1f,M",
365 position_altitude);
368 g_string_append_c(nmea_gga, ','); /* FIXME */
369 g_string_append_c(nmea_gga, ',');
370 g_string_append_c(nmea_gga, ',');
371 g_string_append_c(nmea_gga, ',');
373 add_nmea_checksum(nmea_gga);
376 static void
377 update_nmea_gsa(void)
379 unsigned i;
381 g_string_assign(nmea_gsa, "$GPGSA");
383 g_string_append_c(nmea_gsa, ',');
384 g_string_append_c(nmea_gsa, 'A');
386 g_string_append_printf(nmea_gsa, ",%1d", MAX(get_fixstatus() - 1, 0));
388 for (i = 0; i < 12; i++) {
389 if (i < satellite_satellitec) {
390 g_string_append_printf(nmea_gsa, ",%02d",
391 satellite_satellitev[i].prn);
392 } else {
393 g_string_append_c(nmea_gsa, ',');
397 if (!ACCURACY_PDOP_IS_VALID()) {
398 g_string_append_c(nmea_gsa, ',');
399 } else {
400 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_pdop);
403 if (!ACCURACY_HDOP_IS_VALID()) {
404 g_string_append_c(nmea_gsa, ',');
405 } else {
406 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_hdop);
409 if (!ACCURACY_VDOP_IS_VALID()) {
410 g_string_append_c(nmea_gsa, ',');
411 } else {
412 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_vdop);
415 add_nmea_checksum(nmea_gsa);
418 static void
419 update_nmea_gsv(void)
421 unsigned i;
422 unsigned sat_count;
423 unsigned sentence_count;
425 g_string_truncate(nmea_gsv[0], 0);
426 g_string_truncate(nmea_gsv[1], 0);
427 g_string_truncate(nmea_gsv[2], 0);
429 if (satellite_satellitec == 0) {
430 g_string_assign(nmea_gsv[0], "$GPGSV,1,1,00*79");
431 return;
434 sat_count = MIN(satellite_satellitec, 12);
435 sentence_count = ((sat_count - 1) / 4) + 1;
437 for (i = 0; i < sat_count; i++) {
438 int index_div, index_mod;
440 index_div = i / 4;
441 index_mod = i % 4;
443 if (index_mod == 0) {
444 g_string_append_printf(nmea_gsv[index_div],
445 "$GPGSV,%1d,%1d,%02d",
446 sentence_count,
447 index_div + 1,
448 sat_count);
451 g_string_append_printf(nmea_gsv[index_div],
452 ",%02u,%02u,%03u,%02u",
453 satellite_satellitev[i].prn,
454 satellite_satellitev[i].elevation,
455 satellite_satellitev[i].azimuth,
456 satellite_satellitev[i].snr);
459 for (i = 0; i < 3; i++) {
460 if (nmea_gsv[i]->len > 0) {
461 add_nmea_checksum(nmea_gsv[i]);
466 static void
467 update_nmea_rmc(void)
469 g_string_assign(nmea_rmc, "$GPRMC");
471 add_nmea_time(nmea_rmc);
473 if (get_fixstatus() > 1) {
474 g_string_append_c(nmea_rmc, ',');
475 g_string_append_c(nmea_rmc, 'A');
476 } else {
477 g_string_append_c(nmea_rmc, ',');
478 g_string_append_c(nmea_rmc, 'V');
481 add_nmea_latitude(nmea_rmc);
483 add_nmea_longitude(nmea_rmc);
485 if (!COURSE_SPEED_IS_VALID()) {
486 g_string_append_c(nmea_rmc, ',');
487 } else {
488 g_string_append_printf(nmea_rmc, ",%07.3f",
489 course_speed);
492 if (!COURSE_DIRECTION_IS_VALID()) {
493 g_string_append_c(nmea_rmc, ',');
494 } else {
495 g_string_append_printf(nmea_rmc, ",%06.2f",
496 course_direction);
499 if (time_seconds <= 0) {
500 g_string_append_c(nmea_rmc, ',');
501 } else {
502 struct tm *tm;
503 time_t seconds;
505 seconds = time_seconds;
506 tm = gmtime(&seconds);
508 g_string_append_printf(nmea_rmc, ",%02d%02d%02d",
509 tm->tm_mday, tm->tm_mon + 1, tm->tm_year % 100);
512 g_string_append_c(nmea_rmc, ',');
513 g_string_append_c(nmea_rmc, ',');
515 add_nmea_checksum(nmea_rmc);
518 static void
519 update_nmea_gll(void)
521 g_string_assign(nmea_gll, "$GPGLL");
523 add_nmea_latitude(nmea_gll);
525 add_nmea_longitude(nmea_gll);
527 add_nmea_time(nmea_gll);
529 if (get_fixstatus() > 1) {
530 g_string_append_c(nmea_gll, ',');
531 g_string_append_c(nmea_gll, 'A');
532 } else {
533 g_string_append_c(nmea_gll, ',');
534 g_string_append_c(nmea_gll, 'V');
537 add_nmea_checksum(nmea_gll);
540 static void
541 update_nmea_vtg(void)
543 g_string_assign(nmea_vtg, "$GPVTG");
545 if (!COURSE_DIRECTION_IS_VALID()) {
546 g_string_append_c(nmea_vtg, ',');
547 g_string_append_c(nmea_vtg, ',');
548 } else {
549 g_string_append_printf(nmea_vtg, ",%04.2f,T", course_direction);
552 g_string_append_c(nmea_vtg, ','); /* FIXME */
553 g_string_append_c(nmea_vtg, ',');
555 if (!COURSE_SPEED_IS_VALID()) {
556 g_string_append_c(nmea_vtg, ',');
557 g_string_append_c(nmea_vtg, ',');
558 g_string_append_c(nmea_vtg, ',');
559 g_string_append_c(nmea_vtg, ',');
560 } else {
561 g_string_append_printf(nmea_vtg, ",%05.3f,N,%05.3f,K",
562 course_speed, course_speed * KNOTS_TO_KPH);
565 add_nmea_checksum(nmea_vtg);
568 /******************************************************************************/
570 static gboolean
571 send_nmea_to_clients(gpointer data)
573 GString *string;
574 GList *l;
576 for (string = NULL, l = client_list; l; l = l->next) {
577 struct client *client = l->data;
579 if (client->raw) {
580 if (!string) {
581 static GTimeVal timestamp = { 0, 0 };
583 if (data && check_period(&timestamp, data, 1)) {
584 return FALSE;
587 g_get_current_time(&timestamp);
589 string = g_string_sized_new(1024);
591 if (!nmea_gga->len) {
592 update_nmea_gga();
594 if (!nmea_gsa->len) {
595 update_nmea_gsa();
597 if (!nmea_gsv[0]->len) {
598 update_nmea_gsv();
600 if (!nmea_rmc->len) {
601 update_nmea_rmc();
603 if (!nmea_gll->len) {
604 update_nmea_gll();
606 if (!nmea_vtg->len) {
607 update_nmea_vtg();
610 if (nmea_gga->len) {
611 DEBUG("Sending nmea: %s",
612 nmea_gga->str);
613 g_string_append_len(string,
614 nmea_gga->str,
615 nmea_gga->len);
616 g_string_append_c(string, '\r');
617 g_string_append_c(string, '\n');
619 if (nmea_gsa->len) {
620 DEBUG("Sending nmea: %s",
621 nmea_gsa->str);
622 g_string_append_len(string,
623 nmea_gsa->str,
624 nmea_gsa->len);
625 g_string_append_c(string, '\r');
626 g_string_append_c(string, '\n');
628 if (nmea_gsv[0]->len) {
629 DEBUG("Sending nmea: %s",
630 nmea_gsv[0]->str);
631 g_string_append_len(string,
632 nmea_gsv[0]->str,
633 nmea_gsv[0]->len);
634 g_string_append_c(string, '\r');
635 g_string_append_c(string, '\n');
637 if (nmea_gsv[1]->len) {
638 DEBUG("Sending nmea: %s",
639 nmea_gsv[1]->str);
640 g_string_append_len(string,
641 nmea_gsv[1]->str,
642 nmea_gsv[1]->len);
643 g_string_append_c(string, '\r');
644 g_string_append_c(string, '\n');
646 if (nmea_gsv[2]->len) {
647 DEBUG("Sending nmea: %s",
648 nmea_gsv[2]->str);
649 g_string_append_len(string,
650 nmea_gsv[2]->str,
651 nmea_gsv[2]->len);
652 g_string_append_c(string, '\r');
653 g_string_append_c(string, '\n');
655 if (nmea_rmc->len) {
656 DEBUG("Sending nmea: %s",
657 nmea_rmc->str);
658 g_string_append_len(string,
659 nmea_rmc->str,
660 nmea_rmc->len);
661 g_string_append_c(string, '\r');
662 g_string_append_c(string, '\n');
664 if (nmea_gll->len) {
665 DEBUG("Sending nmea: %s",
666 nmea_gll->str);
667 g_string_append_len(string,
668 nmea_gll->str,
669 nmea_gll->len);
670 g_string_append_c(string, '\r');
671 g_string_append_c(string, '\n');
673 if (nmea_vtg->len) {
674 DEBUG("Sending nmea: %s",
675 nmea_vtg->str);
676 g_string_append_len(string,
677 nmea_vtg->str,
678 nmea_vtg->len);
679 g_string_append_c(string, '\r');
680 g_string_append_c(string, '\n');
684 send_to_client(client, string->str, string->len);
688 if (string) {
689 g_string_free(string, TRUE);
692 nmea_queued = FALSE;
694 return FALSE;
697 static void
698 queue_send_nmea_to_clients(void)
700 if (nmea_queued) {
701 return;
704 nmea_queued = TRUE;
706 g_idle_add(send_nmea_to_clients, NULL);
709 /******************************************************************************/
711 static void
712 add_sats(GString *string)
714 unsigned i, count;
716 if (satellite_satellitec == 0) {
717 g_string_append(string, ",Y=?");
718 return;
721 g_string_append(string, ",Y=-");
723 if (time_seconds <= 0) {
724 g_string_append(string, " ? ");
725 } else {
726 g_string_append_printf(string, " %d.000 ", time_seconds);
729 count = MIN(satellite_satellitec, 12);
731 g_string_append_printf(string, "%d:", count);
733 for (i = 0; i < count; i++) {
734 g_string_append_printf(string, "%d %d %d %d %d:",
735 satellite_satellitev[i].prn,
736 satellite_satellitev[i].elevation,
737 satellite_satellitev[i].azimuth,
738 satellite_satellitev[i].snr,
739 satellite_satellitev[i].used);
743 /******************************************************************************/
745 static gboolean
746 send_sats_to_clients(gpointer data)
748 GString *string;
749 GList *l;
751 for (string = NULL, l = client_list; l; l = l->next) {
752 struct client *client = l->data;
754 if (client->watcher) {
755 if (!string) {
756 static GTimeVal timestamp = { 0, 0 };
758 if (data && check_period(&timestamp, data, 1)) {
759 return FALSE;
762 g_get_current_time(&timestamp);
764 string = g_string_sized_new(1024);
766 g_string_append(string, "GPSD");
768 add_sats(string);
770 DEBUG("Sending sats: %s", string->str);
772 g_string_append_c(string, '\r');
773 g_string_append_c(string, '\n');
776 send_to_client(client, string->str, string->len);
780 if (string) {
781 g_string_free(string, TRUE);
784 sats_queued = FALSE;
786 return FALSE;
789 static void
790 queue_send_sats_to_clients(void)
792 if (sats_queued) {
793 return;
796 sats_queued = TRUE;
798 g_idle_add(send_sats_to_clients, NULL);
801 /******************************************************************************/
803 static void
804 add_info(GString *string)
806 if (!device_connectionstatus || get_fixstatus() <= 1) {
807 g_string_append(string, ",O=?");
808 return;
811 g_string_append(string, ",O=-");
813 if (time_seconds <= 0) {
814 g_string_append_c(string, ' ');
815 g_string_append_c(string, '?');
816 } else {
817 g_string_append_printf(string, " %d.000", time_seconds);
820 g_string_append_c(string, ' '); /* FIXME */
821 g_string_append_c(string, '?');
823 if (!POSITION_LATITUDE_IS_VALID()) {
824 g_string_append_c(string, ' ');
825 g_string_append_c(string, '?');
826 } else {
827 g_string_append_printf(string, " %.6f", position_latitude);
829 if (!POSITION_LONGITUDE_IS_VALID()) {
830 g_string_append_c(string, ' ');
831 g_string_append_c(string, '?');
832 } else {
833 g_string_append_printf(string, " %.6f", position_longitude);
835 if (!POSITION_ALTITUDE_IS_VALID()) {
836 g_string_append_c(string, ' ');
837 g_string_append_c(string, '?');
838 } else {
839 g_string_append_printf(string, " %.2f", position_altitude);
841 if (!ACCURACY_HDOP_IS_VALID()) {
842 g_string_append_c(string, ' ');
843 g_string_append_c(string, '?');
844 } else {
845 g_string_append_printf(string, " %.2f", accuracy_hdop);
847 if (!ACCURACY_VDOP_IS_VALID()) {
848 g_string_append_c(string, ' ');
849 g_string_append_c(string, '?');
850 } else {
851 g_string_append_printf(string, " %.2f", accuracy_vdop);
853 if (!COURSE_DIRECTION_IS_VALID()) {
854 g_string_append_c(string, ' ');
855 g_string_append_c(string, '?');
856 } else {
857 g_string_append_printf(string, " %.4f", course_direction);
859 if (!COURSE_SPEED_IS_VALID()) {
860 g_string_append_c(string, ' ');
861 g_string_append_c(string, '?');
862 } else {
863 g_string_append_printf(string, " %.3f",
864 course_speed * KNOTS_TO_MPS);
866 if (!COURSE_CLIMB_IS_VALID()) {
867 g_string_append_c(string, ' ');
868 g_string_append_c(string, '?');
869 } else {
870 g_string_append_printf(string, " %.3f", course_climb);
873 g_string_append(string, " ? ? ?"); /* FIXME */
875 g_string_append_printf(string, " %d", get_fixstatus());
878 /******************************************************************************/
880 static gboolean
881 send_info_to_clients(gpointer data)
883 GString *string;
884 GList *l;
886 for (string = NULL, l = client_list; l; l = l->next) {
887 struct client *client = l->data;
889 if (client->watcher) {
890 if (!string) {
891 static GTimeVal timestamp = { 0, 0 };
893 if (data && check_period(&timestamp, data, 1)) {
894 return FALSE;
897 g_get_current_time(&timestamp);
899 string = g_string_sized_new(1024);
901 g_string_append(string, "GPSD");
903 add_info(string);
905 DEBUG("Sending info: %s", string->str);
907 g_string_append_c(string, '\r');
908 g_string_append_c(string, '\n');
911 send_to_client(client, string->str, string->len);
915 if (string) {
916 g_string_free(string, TRUE);
919 info_queued = FALSE;
921 return FALSE;
924 static void
925 queue_send_info_to_clients(void)
927 if (info_queued) {
928 return;
931 info_queued = TRUE;
933 g_idle_add(send_info_to_clients, NULL);
936 /******************************************************************************/
938 static gboolean
939 timeout_handler(gpointer data)
941 GTimeVal tv;
943 if (!client_list && !nowait) {
944 return FALSE;
947 g_get_current_time(&tv);
949 send_info_to_clients(&tv);
950 send_sats_to_clients(&tv);
951 send_nmea_to_clients(&tv);
953 if (!device_connectionstatus) {
954 dbus_g_proxy_call_no_reply(proxy_usage, "RequestResource",
955 G_TYPE_STRING, "GPS",
956 G_TYPE_INVALID,
957 G_TYPE_INVALID);
960 return TRUE;
963 /******************************************************************************/
965 static void
966 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE(
967 GClosure *closure,
968 GValue *return_value G_GNUC_UNUSED,
969 guint n_param_values,
970 const GValue *param_values,
971 gpointer invocation_hint G_GNUC_UNUSED,
972 gpointer marshal_data
975 typedef void (*GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE)(
976 gpointer data1,
977 gint arg_1,
978 gint arg_2,
979 gdouble arg_3,
980 gdouble arg_4,
981 gdouble arg_5,
982 gpointer data2);
984 GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE callback;
985 GCClosure *cc = (GCClosure*) closure;
986 gpointer data1, data2;
988 g_return_if_fail(n_param_values == 6);
990 if (G_CCLOSURE_SWAP_DATA(closure)) {
991 data1 = closure->data;
992 data2 = g_value_peek_pointer(param_values + 0);
993 } else {
994 data1 = g_value_peek_pointer(param_values + 0);
995 data2 = closure->data;
998 callback = (GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE) (
999 marshal_data ? marshal_data : cc->callback);
1001 callback(data1,
1002 g_value_get_int(param_values + 1),
1003 g_value_get_int(param_values + 2),
1004 g_value_get_double(param_values + 3),
1005 g_value_get_double(param_values + 4),
1006 g_value_get_double(param_values + 5),
1007 data2);
1010 static void
1011 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE(
1012 GClosure *closure,
1013 GValue *return_value G_GNUC_UNUSED,
1014 guint n_param_values,
1015 const GValue *param_values,
1016 gpointer invocation_hint G_GNUC_UNUSED,
1017 gpointer marshal_data
1020 typedef void (*GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE)(
1021 gpointer data1,
1022 gint arg_1,
1023 gdouble arg_2,
1024 gdouble arg_3,
1025 gdouble arg_4,
1026 gpointer data2);
1028 GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE callback;
1029 GCClosure *cc = (GCClosure*) closure;
1030 gpointer data1, data2;
1032 g_return_if_fail(n_param_values == 5);
1034 if (G_CCLOSURE_SWAP_DATA(closure)) {
1035 data1 = closure->data;
1036 data2 = g_value_peek_pointer(param_values + 0);
1037 } else {
1038 data1 = g_value_peek_pointer(param_values + 0);
1039 data2 = closure->data;
1042 callback = (GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE) (
1043 marshal_data ? marshal_data : cc->callback);
1045 callback(data1,
1046 g_value_get_int(param_values + 1),
1047 g_value_get_double(param_values + 2),
1048 g_value_get_double(param_values + 3),
1049 g_value_get_double(param_values + 4),
1050 data2);
1053 /******************************************************************************/
1055 static void
1056 connection_status_changed(
1057 DBusGProxy *proxy,
1058 gboolean connectionstatus,
1059 gpointer user_data
1062 device_connectionstatus = connectionstatus;
1064 INFO("ConnectionStatusChanged(connectionstatus=%s)",
1065 device_connectionstatus ? "TRUE" : "FALSE");
1068 static void
1069 fix_status_changed(
1070 DBusGProxy *proxy,
1071 int fixstatus,
1072 gpointer user_data
1075 const char *str;
1077 device_fixstatus = fixstatus;
1079 switch (device_fixstatus) {
1080 case 1:
1081 str = "NONE";
1082 break;
1083 case 2:
1084 str = "2D";
1085 break;
1086 case 3:
1087 str = "3D";
1088 break;
1089 default:
1090 str = "INVALID";
1091 break;
1094 INFO("FixStatusChanged(fixstatus=%s)", str);
1096 g_string_truncate(nmea_gga, 0);
1097 g_string_truncate(nmea_gsa, 0);
1098 g_string_truncate(nmea_rmc, 0);
1099 g_string_truncate(nmea_gll, 0);
1101 queue_send_nmea_to_clients();
1102 queue_send_info_to_clients();
1105 static void
1106 position_changed(
1107 DBusGProxy *proxy,
1108 int fields,
1109 int timestamp,
1110 double latitude,
1111 double longitude,
1112 double altitude,
1113 gpointer user_data
1116 position_fields = fields;
1117 position_timestamp = timestamp;
1118 position_latitude = latitude;
1119 position_longitude = longitude;
1120 position_altitude = altitude;
1122 INFO("PositionChanged(timestamp=%d, latitude=%f, longitude=%f, altitude=%f)",
1123 position_timestamp,
1124 POSITION_LATITUDE_IS_VALID() ? position_latitude : NAN,
1125 POSITION_LONGITUDE_IS_VALID() ? position_longitude : NAN,
1126 POSITION_ALTITUDE_IS_VALID() ? position_altitude : NAN);
1128 g_string_truncate(nmea_gga, 0);
1129 g_string_truncate(nmea_rmc, 0);
1130 g_string_truncate(nmea_gll, 0);
1132 queue_send_nmea_to_clients();
1133 queue_send_info_to_clients();
1136 static void
1137 accuracy_changed(
1138 DBusGProxy *proxy,
1139 int fields,
1140 double pdop,
1141 double hdop,
1142 double vdop,
1143 gpointer user_data
1146 accuracy_fields = fields;
1147 accuracy_pdop = pdop;
1148 accuracy_hdop = hdop;
1149 accuracy_vdop = vdop;
1151 INFO("AccuracyChanged(pdop=%f, hdop=%f, vdop=%f)",
1152 ACCURACY_PDOP_IS_VALID() ? accuracy_pdop : NAN,
1153 ACCURACY_HDOP_IS_VALID() ? accuracy_hdop : NAN,
1154 ACCURACY_VDOP_IS_VALID() ? accuracy_vdop : NAN);
1156 g_string_truncate(nmea_gga, 0);
1157 g_string_truncate(nmea_gsa, 0);
1159 queue_send_nmea_to_clients();
1160 queue_send_info_to_clients();
1163 static void
1164 course_changed(
1165 DBusGProxy *proxy,
1166 int fields,
1167 int timestamp,
1168 double speed,
1169 double direction,
1170 double climb,
1171 gpointer user_data
1174 course_fields = fields;
1175 course_timestamp = timestamp;
1176 course_speed = speed;
1177 course_direction = direction;
1178 course_climb = climb;
1180 INFO("CourseChanged(timestamp=%d, speed=%f, direction=%f, climb=%f)",
1181 course_timestamp,
1182 COURSE_SPEED_IS_VALID() ? course_speed : NAN,
1183 COURSE_DIRECTION_IS_VALID() ? course_direction : NAN,
1184 COURSE_CLIMB_IS_VALID() ? course_climb : NAN);
1186 g_string_truncate(nmea_rmc, 0);
1187 g_string_truncate(nmea_vtg, 0);
1189 queue_send_nmea_to_clients();
1190 queue_send_info_to_clients();
1193 static void
1194 satellites_changed(
1195 DBusGProxy *proxy,
1196 GPtrArray *satellites,
1197 gpointer user_data
1200 int i;
1202 if (!satellites) {
1203 satellite_satellitec = 0;
1204 } else {
1205 satellite_satellitec = MIN(SATELLITEC_MAX, satellites->len);
1208 for (i = 0; i < satellite_satellitec; i++) {
1209 struct satellite *sat;
1210 GValueArray *val;
1212 sat = satellite_satellitev + i;
1213 val = satellites->pdata[i];
1215 sat->prn = g_value_get_uint(g_value_array_get_nth(val, 0));
1216 sat->used = g_value_get_boolean(g_value_array_get_nth(val, 1));
1217 sat->elevation = g_value_get_uint(g_value_array_get_nth(val, 2));
1218 sat->azimuth = g_value_get_uint(g_value_array_get_nth(val, 3));
1219 sat->snr = g_value_get_uint(g_value_array_get_nth(val, 4));
1221 INFO("SatellitesChanged(index=%d, prn=%u, used=%s, elevation=%u, azimuth=%u, snr=%u)",
1223 sat->prn,
1224 sat->used ? "TRUE" : "FALSE",
1225 sat->elevation,
1226 sat->azimuth,
1227 sat->snr);
1230 g_string_truncate(nmea_gga, 0);
1231 g_string_truncate(nmea_gsa, 0);
1232 g_string_truncate(nmea_gsv[0], 0);
1233 g_string_truncate(nmea_gsv[1], 0);
1234 g_string_truncate(nmea_gsv[2], 0);
1236 queue_send_nmea_to_clients();
1237 queue_send_sats_to_clients();
1240 static void
1241 time_changed(
1242 DBusGProxy *proxy,
1243 int seconds,
1244 gpointer user_data
1247 time_seconds = seconds;
1249 INFO("TimeChanged(seconds=%d)", time_seconds);
1251 g_string_truncate(nmea_gga, 0);
1252 g_string_truncate(nmea_rmc, 0);
1253 g_string_truncate(nmea_gll, 0);
1255 queue_send_nmea_to_clients();
1256 queue_send_info_to_clients();
1259 /******************************************************************************/
1261 static void
1262 start(void)
1264 dbus_g_proxy_connect_signal(proxy_device, "ConnectionStatusChanged",
1265 G_CALLBACK(connection_status_changed), NULL, NULL);
1266 dbus_g_proxy_connect_signal(proxy_device, "FixStatusChanged",
1267 G_CALLBACK(fix_status_changed), NULL, NULL);
1268 dbus_g_proxy_connect_signal(proxy_position, "PositionChanged",
1269 G_CALLBACK(position_changed), NULL, NULL);
1270 dbus_g_proxy_connect_signal(proxy_accuracy, "AccuracyChanged",
1271 G_CALLBACK(accuracy_changed), NULL, NULL);
1272 dbus_g_proxy_connect_signal(proxy_course, "CourseChanged",
1273 G_CALLBACK(course_changed), NULL, NULL);
1274 dbus_g_proxy_connect_signal(proxy_satellite, "SatellitesChanged",
1275 G_CALLBACK(satellites_changed), NULL, NULL);
1276 dbus_g_proxy_connect_signal(proxy_time, "TimeChanged",
1277 G_CALLBACK(time_changed), NULL, NULL);
1279 dbus_g_proxy_call_no_reply(proxy_usage, "RequestResource",
1280 G_TYPE_STRING, "GPS",
1281 G_TYPE_INVALID,
1282 G_TYPE_INVALID);
1285 gboolean connectionstatus = FALSE;
1287 dbus_g_proxy_call(proxy_device, "GetConnectionStatus", NULL,
1288 G_TYPE_INVALID,
1289 G_TYPE_BOOLEAN, &connectionstatus,
1290 G_TYPE_INVALID);
1291 connection_status_changed(NULL, connectionstatus, NULL);
1294 int fixstatus = 0;
1296 dbus_g_proxy_call(proxy_device, "GetFixStatus", NULL,
1297 G_TYPE_INVALID,
1298 G_TYPE_INT, &fixstatus,
1299 G_TYPE_INVALID);
1300 fix_status_changed(NULL, fixstatus, NULL);
1303 int fields = 0, timestamp = 0;
1304 double latitude = NAN, longitude = NAN, altitude = NAN;
1306 dbus_g_proxy_call(proxy_position, "GetPosition", NULL,
1307 G_TYPE_INVALID,
1308 G_TYPE_INT, &fields,
1309 G_TYPE_INT, &timestamp,
1310 G_TYPE_DOUBLE, &latitude,
1311 G_TYPE_DOUBLE, &longitude,
1312 G_TYPE_DOUBLE, &altitude,
1313 G_TYPE_INVALID);
1314 position_changed(NULL, fields, timestamp, latitude, longitude,
1315 altitude, NULL);
1318 int fields = 0;
1319 double pdop = NAN, hdop = NAN, vdop = NAN;
1321 dbus_g_proxy_call(proxy_accuracy, "GetAccuracy", NULL,
1322 G_TYPE_INVALID,
1323 G_TYPE_INT, &fields,
1324 G_TYPE_DOUBLE, &pdop,
1325 G_TYPE_DOUBLE, &hdop,
1326 G_TYPE_DOUBLE, &vdop,
1327 G_TYPE_INVALID);
1328 accuracy_changed(NULL, fields, pdop, hdop, vdop, NULL);
1331 int fields = 0, timestamp = 0;
1332 double speed = NAN, direction = NAN, climb = NAN;
1334 dbus_g_proxy_call(proxy_course, "GetCourse", NULL,
1335 G_TYPE_INVALID,
1336 G_TYPE_INT, &fields,
1337 G_TYPE_INT, &timestamp,
1338 G_TYPE_DOUBLE, &speed,
1339 G_TYPE_DOUBLE, &direction,
1340 G_TYPE_DOUBLE, &climb,
1341 G_TYPE_INVALID);
1342 course_changed(NULL, fields, timestamp, speed, direction,
1343 climb, NULL);
1346 GPtrArray *satellites = NULL;
1348 dbus_g_proxy_call(proxy_satellite, "GetSatellites", NULL,
1349 G_TYPE_INVALID,
1350 dbus_g_type_get_collection("GPtrArray",
1351 dbus_g_type_get_struct("GValueArray",
1352 G_TYPE_UINT,
1353 G_TYPE_BOOLEAN,
1354 G_TYPE_UINT,
1355 G_TYPE_UINT,
1356 G_TYPE_UINT,
1357 G_TYPE_INVALID)),
1358 &satellites,
1359 G_TYPE_INVALID);
1360 satellites_changed(NULL, satellites, NULL);
1363 int seconds;
1365 dbus_g_proxy_call(proxy_time, "GetTime", NULL,
1366 G_TYPE_INVALID,
1367 G_TYPE_INT, &seconds,
1368 G_TYPE_INVALID);
1369 time_changed(NULL, seconds, NULL);
1372 g_timeout_add(1000, timeout_handler, NULL);
1375 static void
1376 stop(void)
1378 dbus_g_proxy_call_no_reply(proxy_usage, "ReleaseResource",
1379 G_TYPE_STRING, "GPS",
1380 G_TYPE_INVALID,
1381 G_TYPE_INVALID);
1383 dbus_g_proxy_disconnect_signal(proxy_device, "ConnectionStatusChanged",
1384 G_CALLBACK(connection_status_changed), NULL);
1385 dbus_g_proxy_disconnect_signal(proxy_device, "FixStatusChanged",
1386 G_CALLBACK(fix_status_changed), NULL);
1387 dbus_g_proxy_disconnect_signal(proxy_position, "PositionChanged",
1388 G_CALLBACK(position_changed), NULL);
1389 dbus_g_proxy_disconnect_signal(proxy_accuracy, "AccuracyChanged",
1390 G_CALLBACK(accuracy_changed), NULL);
1391 dbus_g_proxy_disconnect_signal(proxy_course, "CourseChanged",
1392 G_CALLBACK(course_changed), NULL);
1393 dbus_g_proxy_disconnect_signal(proxy_satellite, "SatellitesChanged",
1394 G_CALLBACK(satellites_changed), NULL);
1395 dbus_g_proxy_disconnect_signal(proxy_time, "TimeChanged",
1396 G_CALLBACK(time_changed), NULL);
1399 /******************************************************************************/
1401 static void
1402 init_dbus(void)
1404 GError *error = NULL;
1406 connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
1408 if (!connection) {
1409 ERROR("Failed to open connection to system bus: %s\n",
1410 error->message);
1413 dbus_g_object_register_marshaller(
1414 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE,
1415 G_TYPE_NONE,
1416 G_TYPE_INT,
1417 G_TYPE_DOUBLE,
1418 G_TYPE_DOUBLE,
1419 G_TYPE_DOUBLE,
1420 G_TYPE_INVALID);
1421 dbus_g_object_register_marshaller(
1422 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE,
1423 G_TYPE_NONE,
1424 G_TYPE_INT,
1425 G_TYPE_INT,
1426 G_TYPE_DOUBLE,
1427 G_TYPE_DOUBLE,
1428 G_TYPE_DOUBLE,
1429 G_TYPE_INVALID);
1431 proxy_usage = dbus_g_proxy_new_for_name(connection, OUSAGED, USAGE,
1432 "org.freesmartphone.Usage");
1433 proxy_device = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1434 "org.freedesktop.Gypsy.Device");
1435 proxy_position = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1436 "org.freedesktop.Gypsy.Position");
1437 proxy_accuracy = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1438 "org.freedesktop.Gypsy.Accuracy");
1439 proxy_course = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1440 "org.freedesktop.Gypsy.Course");
1441 proxy_satellite = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1442 "org.freedesktop.Gypsy.Satellite");
1443 proxy_time = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1444 "org.freedesktop.Gypsy.Time");
1446 dbus_g_proxy_add_signal(proxy_device, "ConnectionStatusChanged",
1447 G_TYPE_BOOLEAN,
1448 G_TYPE_INVALID);
1449 dbus_g_proxy_add_signal(proxy_device, "FixStatusChanged",
1450 G_TYPE_INT,
1451 G_TYPE_INVALID);
1452 dbus_g_proxy_add_signal(proxy_position, "PositionChanged",
1453 G_TYPE_INT,
1454 G_TYPE_INT,
1455 G_TYPE_DOUBLE,
1456 G_TYPE_DOUBLE,
1457 G_TYPE_DOUBLE,
1458 G_TYPE_INVALID);
1459 dbus_g_proxy_add_signal(proxy_accuracy, "AccuracyChanged",
1460 G_TYPE_INT,
1461 G_TYPE_DOUBLE,
1462 G_TYPE_DOUBLE,
1463 G_TYPE_DOUBLE,
1464 G_TYPE_INVALID);
1465 dbus_g_proxy_add_signal(proxy_course, "CourseChanged",
1466 G_TYPE_INT,
1467 G_TYPE_INT,
1468 G_TYPE_DOUBLE,
1469 G_TYPE_DOUBLE,
1470 G_TYPE_DOUBLE,
1471 G_TYPE_INVALID);
1472 dbus_g_proxy_add_signal(proxy_satellite, "SatellitesChanged",
1473 dbus_g_type_get_collection("GPtrArray",
1474 dbus_g_type_get_struct("GValueArray",
1475 G_TYPE_UINT,
1476 G_TYPE_BOOLEAN,
1477 G_TYPE_UINT,
1478 G_TYPE_UINT,
1479 G_TYPE_UINT,
1480 G_TYPE_INVALID)),
1481 G_TYPE_INVALID);
1482 dbus_g_proxy_add_signal(proxy_time, "TimeChanged",
1483 G_TYPE_INT,
1484 G_TYPE_INVALID);
1487 /******************************************************************************/
1489 static gboolean
1490 read_from_client(GIOChannel *source, GIOCondition condition, gpointer data)
1492 struct client *client;
1493 char buf[4096];
1494 gsize len = sizeof(buf);
1495 GIOStatus status;
1496 GString *reply;
1497 char *p;
1499 client = data;
1501 if (condition != G_IO_IN) {
1502 shutdown_client(client);
1505 if (!client->channel) {
1506 MESSAGE("Removing client");
1508 client_list = g_list_remove(client_list, client);
1509 g_free(client);
1510 g_io_channel_unref(source);
1512 if (!client_list && !nowait) {
1513 stop();
1516 return FALSE;
1519 status = g_io_channel_read_chars(source, buf, len - 2, &len, NULL);
1521 if ((status != G_IO_STATUS_NORMAL) || (len < 1)) {
1522 shutdown_client(client);
1523 return TRUE;
1526 if (buf[len - 1] != '\n') {
1527 buf[len] = '\n';
1528 len++;
1531 buf[len] = '\0';
1533 reply = g_string_sized_new(128);
1534 g_string_assign(reply, "GPSD");
1536 p = buf;
1538 while (*p) {
1539 switch (toupper(*p++)) {
1540 case 'A':
1541 if (!POSITION_ALTITUDE_IS_VALID()) {
1542 g_string_append(reply, ",A=?");
1543 } else {
1544 g_string_append_printf(reply, ",A=%.3f",
1545 position_altitude);
1547 break;
1548 case 'B':
1549 g_string_append(reply, ",B=?");
1550 break;
1551 case 'C':
1552 g_string_append(reply, ",C=1.00"); /* FIXME */
1553 break;
1554 case 'D':
1555 if (time_seconds <= 0) {
1556 g_string_append(reply, ",D=?");
1557 } else {
1558 struct tm *tm;
1559 time_t seconds;
1561 seconds = time_seconds;
1562 tm = gmtime(&seconds);
1564 g_string_append_printf(reply,
1565 ",D=%04d-%02d-%02dT%02d:%02d:%02d.00Z",
1566 tm->tm_year + 1900, tm->tm_mon + 1,
1567 tm->tm_mday, tm->tm_hour, tm->tm_min,
1568 tm->tm_sec);
1570 break;
1571 case 'E':
1572 g_string_append(reply, ",E=");
1574 if (!ACCURACY_PDOP_IS_VALID()) {
1575 g_string_append(reply, "?");
1576 } else {
1577 g_string_append_printf(reply, "%.2f",
1578 accuracy_pdop);
1580 if (!ACCURACY_HDOP_IS_VALID()) {
1581 g_string_append(reply, " ?");
1582 } else {
1583 g_string_append_printf(reply, " %.2f",
1584 accuracy_hdop);
1586 if (!ACCURACY_VDOP_IS_VALID()) {
1587 g_string_append(reply, " ?");
1588 } else {
1589 g_string_append_printf(reply, " %.2f",
1590 accuracy_vdop);
1592 break;
1593 case 'F':
1594 if (*p == '=') {
1595 while (isprint(*p) && !isspace(*p)) {
1596 p++;
1599 g_string_append(reply, ",F=gypsy");
1600 break;
1601 case 'G':
1602 if (*p == '=') {
1603 p += strcspn(p, ",\r\n");
1605 g_string_append(reply, ",G=GPS");
1606 break;
1607 case 'I':
1608 g_string_append(reply, ",I=gypsy");
1609 break;
1610 case 'J':
1611 if (*p == '=') {
1612 p++;
1614 if (*p == '1' || *p == '+') {
1615 p++;
1616 g_string_append(reply, ",J=1");
1617 } else {
1618 if (*p == '0' || *p == '-') {
1619 p++;
1621 g_string_append(reply, ",J=0");
1623 break;
1624 case 'K':
1625 g_string_append(reply, ",K=1 gypsy");
1626 break;
1627 case 'L':
1628 g_string_append(reply, ",L=3 " VERSION
1629 " abcdefgijklmnopqrstuvwxyz");
1630 break;
1631 case 'M':
1632 g_string_append_printf(reply, ",M=%d",
1633 get_fixstatus());
1634 break;
1635 case 'N':
1636 g_string_append(reply, ",N=0");
1637 break;
1638 case 'O':
1639 add_info(reply);
1640 break;
1641 case 'P':
1642 if (!POSITION_LATITUDE_IS_VALID()
1643 || !POSITION_LONGITUDE_IS_VALID()) {
1644 g_string_append(reply, ",P=?");
1645 } else {
1646 g_string_append_printf(reply, ",P=%.6f %.6f",
1647 position_latitude, position_longitude);
1649 break;
1650 case 'Q':
1651 if (!ACCURACY_PDOP_IS_VALID()
1652 && !ACCURACY_HDOP_IS_VALID()
1653 && !ACCURACY_VDOP_IS_VALID()) {
1654 g_string_append(reply, ",Q=?");
1655 } else {
1656 unsigned i, used;
1658 for (i = 0, used = 0; i < satellite_satellitec; i++) {
1659 if (satellite_satellitev[i].used) {
1660 used++;
1664 g_string_append_printf(reply,
1665 ",Q=%d %.2f %.2f %.2f %.2f %.2f", used,
1666 ACCURACY_PDOP_IS_VALID() ? accuracy_pdop : 0.0,
1667 ACCURACY_HDOP_IS_VALID() ? accuracy_hdop : 0.0,
1668 ACCURACY_VDOP_IS_VALID() ? accuracy_vdop : 0.0,
1669 0.0, 0.0);
1671 break;
1672 case 'R':
1673 if (*p == '=') {
1674 p++;
1676 if (*p == '2' || *p == '1' || *p == '+') {
1677 client->raw = TRUE;
1678 p++;
1679 } else if (*p == '0' || *p == '-') {
1680 client->raw = FALSE;
1681 p++;
1682 } else {
1683 client->raw = !client->raw;
1685 if (client->raw) {
1686 queue_send_nmea_to_clients();
1688 g_string_append_printf(reply, ",R=%1d", client->raw);
1689 break;
1690 case 'S':
1691 g_string_append_printf(reply, ",S=%d",
1692 MAX(get_fixstatus() - 1, 0));
1693 break;
1694 case 'T':
1695 if (!COURSE_DIRECTION_IS_VALID()) {
1696 g_string_append(reply, ",T=?");
1697 } else {
1698 g_string_append_printf(reply, ",T=%.4f",
1699 course_direction);
1701 break;
1702 case 'U':
1703 if (!COURSE_CLIMB_IS_VALID()) {
1704 g_string_append(reply, ",U=?");
1705 } else {
1706 g_string_append_printf(reply, ",U=%.3f",
1707 course_climb);
1709 break;
1710 case 'V':
1711 if (!COURSE_SPEED_IS_VALID()) {
1712 g_string_append(reply, ",V=?");
1713 } else {
1714 g_string_append_printf(reply, ",V=%.3f",
1715 course_speed * KNOTS_TO_MPS);
1717 break;
1718 case 'W':
1719 if (*p == '=') {
1720 p++;
1722 if (*p == '1' || *p == '+') {
1723 client->watcher = TRUE;
1724 p++;
1725 } else if (*p == '0' || *p == '-') {
1726 client->watcher = FALSE;
1727 p++;
1728 } else {
1729 client->watcher = !client->watcher;
1731 if (client->watcher) {
1732 queue_send_sats_to_clients();
1733 queue_send_info_to_clients();
1735 g_string_append_printf(reply, ",W=%1d", client->watcher);
1736 break;
1737 case 'X':
1738 if (time_seconds <= 0) {
1739 g_string_append(reply, ",X=?");
1740 } else {
1741 g_string_append_printf(reply, ",X=%d.000000",
1742 time_seconds);
1744 break;
1745 case 'Y':
1746 add_sats(reply);
1747 break;
1748 case 'Z':
1749 g_string_append(reply, ",Z=?"); /* FIXME */
1750 break;
1751 case '$':
1752 g_string_append(reply, ",$=?"); /* FIXME */
1753 break;
1754 case '\r':
1755 case '\n':
1756 goto finish;
1757 default:
1758 break;
1762 finish:
1763 g_string_append(reply, "\r\n");
1764 send_to_client(client, reply->str, reply->len);
1765 g_string_free(reply, TRUE);
1766 return TRUE;
1769 static gboolean
1770 accept_new_client(GIOChannel *source, GIOCondition condition, gpointer data)
1772 struct sockaddr_in addr;
1773 socklen_t len = sizeof(addr);
1774 struct client *client;
1775 int fd;
1777 MESSAGE("New client request");
1779 fd = accept(g_io_channel_unix_get_fd(source),
1780 (struct sockaddr *) &addr, &len);
1782 if (fd < 0) {
1783 ERROR("Cannot accept new connection: %s", strerror(errno));
1786 client = g_new0(struct client, 1);
1788 if (!client) {
1789 ERROR("Cannot allocate memory");
1792 if (!client_list && !nowait) {
1793 start();
1796 client_list = g_list_prepend(client_list, client);
1798 client->channel = g_io_channel_unix_new(fd);
1800 g_io_channel_set_flags(client->channel, G_IO_FLAG_NONBLOCK, NULL);
1801 g_io_channel_set_encoding(client->channel, NULL, NULL);
1802 g_io_channel_set_buffered(client->channel, FALSE);
1804 g_io_add_watch(client->channel,
1805 G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
1806 read_from_client, client);
1808 return TRUE;
1811 static void
1812 init_service(const char *service)
1814 struct servent *pse;
1815 struct sockaddr_in addr;
1816 GIOChannel *channel;
1817 int fd, one = 1;
1819 memset(&addr, 0, sizeof(addr));
1821 addr.sin_family = AF_INET;
1822 addr.sin_addr.s_addr = INADDR_ANY;
1824 if (!service) {
1825 service = getservbyname("gpsd", "tcp") ? "gpsd" : PORT;
1828 pse = getservbyname(service, "tcp");
1830 if (pse) {
1831 addr.sin_port = pse->s_port;
1832 } else {
1833 addr.sin_port = htons(atoi(service));
1836 if (!addr.sin_port) {
1837 ERROR("Cannot get service entry: %s", service);
1840 fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1842 if (fd < 0) {
1843 ERROR("Cannot create socket: %s", strerror(errno));
1846 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) & one, sizeof(one));
1848 if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
1849 ERROR("Cannot bind to port %s: %s", service, strerror(errno));
1852 if (listen(fd, 4) < 0) {
1853 ERROR("Cannot listen on port %s: %s",
1854 service, strerror(errno));
1857 channel = g_io_channel_unix_new(fd);
1859 g_io_channel_set_encoding(channel, NULL, NULL);
1860 g_io_channel_set_buffered(channel, FALSE);
1862 g_io_add_watch(channel, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR,
1863 accept_new_client, NULL);
1866 /******************************************************************************/
1868 static void
1869 printerr_handler(const char *message)
1871 if (background) {
1872 syslog(LOG_USER | LOG_INFO, "%s", message);
1873 } else {
1874 fputs(message, stderr);
1878 static void
1879 log_handler(
1880 const char *domain,
1881 GLogLevelFlags level,
1882 const char *message,
1883 gpointer user_data
1886 const char *level_string;
1888 switch (level & G_LOG_LEVEL_MASK) {
1889 case G_LOG_LEVEL_ERROR:
1890 level_string = "ERROR: ";
1891 break;
1892 case G_LOG_LEVEL_CRITICAL:
1893 level_string = "CRITICAL: ";
1894 break;
1895 case G_LOG_LEVEL_WARNING:
1896 level_string = "WARNING: ";
1897 break;
1898 case G_LOG_LEVEL_MESSAGE:
1899 level_string = "MESSAGE: ";
1900 break;
1901 case G_LOG_LEVEL_INFO:
1902 level_string = "INFO: ";
1903 break;
1904 case G_LOG_LEVEL_DEBUG:
1905 level_string = "DEBUG: ";
1906 break;
1907 default:
1908 level_string = "";
1909 break;
1912 if (background) {
1913 if (domain) {
1914 syslog(LOG_USER | LOG_INFO, "%s: %s%s", domain,
1915 level_string, message);
1916 } else {
1917 syslog(LOG_USER | LOG_INFO, "%s%s",
1918 level_string, message);
1920 } else {
1921 struct timeval tv = { 0, 0 };
1923 gettimeofday(&tv, NULL);
1925 if (domain) {
1926 fprintf(stderr, "%li.%06li %s: %s%s\n",
1927 (long) tv.tv_sec, (long) tv.tv_usec,
1928 domain, level_string, message);
1929 } else {
1930 fprintf(stderr, "%li.%06li %s%s\n",
1931 (long) tv.tv_sec, (long) tv.tv_usec,
1932 level_string, message);
1937 /******************************************************************************/
1939 static void
1940 usage(void)
1942 printf("usage: "NAME" [-n] [-N] [-D n] [-P pidfile] [-S port] [-h]\n\
1943 Options include:\n\
1944 -n = don't wait for client connects to poll GPS\n\
1945 -N = don't go into background\n\
1946 -P pidfile = set file to record process ID\n\
1947 -D integer (default 0) = set debug level\n\
1948 -S integer (default "PORT") = set port for daemon\n\
1949 -h = help message\n\
1950 -V = emit version and exit.\n");
1953 /******************************************************************************/
1956 main(int argc, char **argv)
1958 GMainLoop *loop = NULL;
1959 char *service = NULL;
1960 char *pid_file = NULL;
1962 g_set_prgname(NAME);
1963 g_set_printerr_handler(printerr_handler);
1964 g_log_set_default_handler(log_handler, NULL);
1966 opterr = 0;
1967 while (1) {
1968 int option;
1970 option = getopt(argc, argv, "F:D:S:bhNnP:VR:");
1972 if (option == -1) {
1973 break;
1976 switch (option) {
1977 case 'F':
1978 case 'b':
1979 case 'R':
1980 WARNING("Not implemented: -%c", option);
1981 break;
1982 case 'D':
1983 switch (atoi(optarg)) {
1984 case 0:
1985 log_level = G_LOG_LEVEL_ERROR
1986 | G_LOG_LEVEL_CRITICAL
1987 | G_LOG_LEVEL_WARNING;
1988 break;
1989 case 1:
1990 log_level = G_LOG_LEVEL_ERROR
1991 | G_LOG_LEVEL_CRITICAL
1992 | G_LOG_LEVEL_WARNING
1993 | G_LOG_LEVEL_MESSAGE;
1994 break;
1995 case 2:
1996 log_level = G_LOG_LEVEL_ERROR
1997 | G_LOG_LEVEL_CRITICAL
1998 | G_LOG_LEVEL_WARNING
1999 | G_LOG_LEVEL_MESSAGE
2000 | G_LOG_LEVEL_INFO;
2001 break;
2002 default:
2003 log_level = G_LOG_LEVEL_ERROR
2004 | G_LOG_LEVEL_CRITICAL
2005 | G_LOG_LEVEL_WARNING
2006 | G_LOG_LEVEL_MESSAGE
2007 | G_LOG_LEVEL_INFO
2008 | G_LOG_LEVEL_DEBUG;
2009 break;
2011 break;
2012 case 'N':
2013 background = FALSE;
2014 break;
2015 case 'S':
2016 service = optarg;
2017 break;
2018 case 'n':
2019 nowait = TRUE;
2020 break;
2021 case 'P':
2022 pid_file = optarg;
2023 break;
2024 case 'V':
2025 printf(NAME" "VERSION"\n");
2026 exit(EXIT_SUCCESS);
2027 break;
2028 case 'h':
2029 case '?':
2030 default:
2031 usage();
2032 exit(EXIT_SUCCESS);
2033 break;
2037 if (background) {
2038 pid_t pid;
2039 int fd;
2041 openlog(NAME, LOG_PID, LOG_USER);
2043 pid = fork();
2045 if (pid < 0) {
2046 ERROR("fork() failed");
2047 } else if (pid > 0) {
2048 exit(EXIT_SUCCESS);
2051 if (setsid() < 0) {
2052 ERROR("setsid() failed");
2055 chdir("/");
2057 fd = open("/dev/null", O_RDWR, 0);
2059 if (fd < 0) {
2060 ERROR("Cannot open /dev/null");
2063 dup2(fd, STDIN_FILENO);
2064 dup2(fd, STDOUT_FILENO);
2065 dup2(fd, STDERR_FILENO);
2067 close(fd);
2070 if (pid_file) {
2071 FILE *fp;
2073 fp = fopen(pid_file, "w");
2075 if (!fp) {
2076 ERROR("Cannot create PID file: %s", pid_file);
2079 fprintf(fp, "%u\n", (unsigned) getpid());
2080 fclose(fp);
2083 g_type_init();
2085 loop = g_main_loop_new(NULL, FALSE);
2087 nmea_gga = g_string_new(NULL);
2088 nmea_gsa = g_string_new(NULL);
2089 nmea_gsv[0] = g_string_new(NULL);
2090 nmea_gsv[1] = g_string_new(NULL);
2091 nmea_gsv[2] = g_string_new(NULL);
2092 nmea_rmc = g_string_new(NULL);
2093 nmea_gll = g_string_new(NULL);
2094 nmea_vtg = g_string_new(NULL);
2096 init_service(service);
2098 init_dbus();
2100 if (nowait) {
2101 start();
2104 g_main_loop_run(loop);
2106 return 0;