Fixed NMEA latitude and longitude generation for negative values
[fso-gpsd.git] / fso-gpsd.c
blob205ef07c2dc4f6436c42d8e5164663d84cedea3e
1 /* fso-gpsd - a gpsd replacement which connects to 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 <dbus/dbus.h>
33 #include <dbus/dbus-glib.h>
34 #include <glib-object.h>
35 #include <glib.h>
37 #define KNOTS (1.852)
39 /******************************************************************************/
41 #define NAME "fso-gpsd"
42 #define PORT "2947"
43 #define OUSAGED "org.freesmartphone.ousaged"
44 #define OGPSD "org.freesmartphone.ogpsd"
45 #define USAGE "/org/freesmartphone/Usage"
46 #define GYPSY "/org/freedesktop/Gypsy"
48 /******************************************************************************/
50 struct client {
51 GIOChannel *channel;
52 gboolean watcher;
53 gboolean raw;
56 struct satellite {
57 unsigned prn;
58 gboolean used;
59 unsigned elevation;
60 unsigned azimuth;
61 unsigned snr;
64 /******************************************************************************/
66 static gboolean nowait = FALSE;
68 static gboolean info_queued = FALSE;
69 static gboolean sats_queued = FALSE;
70 static gboolean nmea_queued = FALSE;
72 /******************************************************************************/
74 static GList *client_list = NULL;
76 /******************************************************************************/
78 static DBusGConnection *connection;
79 static DBusGProxy *proxy_usage;
80 static DBusGProxy *proxy_device;
81 static DBusGProxy *proxy_position;
82 static DBusGProxy *proxy_accuracy;
83 static DBusGProxy *proxy_course;
84 static DBusGProxy *proxy_satellite;
85 static DBusGProxy *proxy_time;
87 /******************************************************************************/
89 static gboolean device_connectionstatus;
90 static int device_fixstatus; /* 0=INVALID, 1=NONE, 2=2D, 3=3D */
92 static int position_timestamp;
93 static double position_latitude;
94 static double position_longitude;
95 static double position_altitude;
97 static double accuracy_pdop;
98 static double accuracy_hdop;
99 static double accuracy_vdop;
101 static int course_timestamp;
102 static double course_speed;
103 static double course_direction;
104 static double course_climb;
106 #define SATELLITEC_MAX 32
107 static struct satellite satellite_satellitev[SATELLITEC_MAX];
108 static unsigned satellite_satellitec;
110 static int time_seconds;
112 /******************************************************************************/
114 static GString *nmea_gga;
115 static GString *nmea_gsa;
116 static GString *nmea_gsv[3];
117 static GString *nmea_rmc;
118 static GString *nmea_gll;
119 static GString *nmea_vtg;
121 /******************************************************************************/
123 static int
124 get_fixstatus(void)
126 if (!isnan(position_latitude) && !isnan(position_longitude)) {
127 if (!isnan(position_altitude)) {
128 return MIN(device_fixstatus, 3);
129 } else {
130 return MIN(device_fixstatus, 2);
132 } else {
133 return MIN(device_fixstatus, 1);
137 /******************************************************************************/
139 static void
140 stop(void);
142 static void
143 remove_client(struct client *client)
145 g_message("Removing client");
147 client_list = g_list_remove(client_list, client);
149 g_io_channel_shutdown(client->channel, FALSE, NULL);
150 g_io_channel_unref(client->channel);
152 g_free(client);
154 if (!client_list && !nowait) {
155 stop();
159 static void
160 send_to_client(struct client *client, const char *string, gsize len)
162 GIOStatus status;
163 gsize bytes_written;
165 /* TODO: check bytes_written */
166 status = g_io_channel_write_chars(client->channel, string,
167 len, &bytes_written, NULL);
169 if (status == G_IO_STATUS_ERROR) {
170 remove_client(client);
174 /******************************************************************************/
176 static void
177 add_nmea_checksum(GString *nmea)
179 int checksum = 0;
180 char *p;
182 p = nmea->str;
184 if (*p == '$') {
185 p++;
188 while (*p) {
189 checksum ^= (int) *p;
190 p++;
193 g_string_append_printf(nmea, "*%02x\r\n", checksum);
196 static void
197 add_nmea_time(GString *nmea)
199 if (time_seconds <= 0) {
200 g_string_append_c(nmea, ',');
201 } else {
202 struct tm *tm;
203 time_t seconds;
205 seconds = time_seconds;
206 tm = gmtime(&seconds);
208 g_string_append_printf(nmea, ",%02d%02d%02d.00",
209 tm->tm_hour, tm->tm_min, tm->tm_sec);
213 static void
214 add_nmea_latitude(GString *nmea)
216 if (isnan(position_latitude)) {
217 g_string_append(nmea, ",,");
218 } else {
219 double a, d;
221 a = fabs(position_latitude);
222 d = floor(a);
224 g_string_append_printf(nmea, ",%010.5f,%C",
225 (d * 100.0) + ((a - d) * 60.0),
226 (position_latitude > 0) ? 'N' : 'S');
230 static void
231 add_nmea_longitude(GString *nmea)
233 if (isnan(position_longitude)) {
234 g_string_append(nmea, ",,");
235 } else {
236 double a, d;
238 a = fabs(position_longitude);
239 d = floor(a);
241 g_string_append_printf(nmea, ",%011.5f,%C",
242 (d * 100.0) + ((a - d) * 60.0),
243 (position_longitude > 0) ? 'E' : 'W');
247 /******************************************************************************/
249 static void
250 update_nmea_gga(void)
252 unsigned i, used;
254 g_string_assign(nmea_gga, "$GPGGA");
256 add_nmea_time(nmea_gga);
258 add_nmea_latitude(nmea_gga);
260 add_nmea_longitude(nmea_gga);
262 g_string_append_printf(nmea_gga, ",%1d", MAX(get_fixstatus() - 1, 0));
264 for (i = 0, used = 0; i < satellite_satellitec; i++) {
265 if (satellite_satellitev[i].used) {
266 used++;
270 g_string_append_printf(nmea_gga, ",%02d", used);
272 if (isnan(accuracy_hdop)) {
273 g_string_append_c(nmea_gga, ',');
274 } else {
275 g_string_append_printf(nmea_gga, ",%04.2f", accuracy_hdop);
278 if (isnan(position_altitude)) {
279 g_string_append(nmea_gga, ",,");
280 } else {
281 g_string_append_printf(nmea_gga, ",%03.1f,M",
282 position_altitude);
285 g_string_append(nmea_gga, ",,"); /* FIXME */
287 g_string_append(nmea_gga, ",,");
289 add_nmea_checksum(nmea_gga);
292 static void
293 update_nmea_gsa(void)
295 unsigned i;
297 g_string_assign(nmea_gsa, "$GPGSA");
299 g_string_append(nmea_gsa, ",A");
301 g_string_append_printf(nmea_gsa, ",%1d", MAX(get_fixstatus() - 1, 0));
303 for (i = 0; i < 12; i++) {
304 if (i < satellite_satellitec) {
305 g_string_append_printf(nmea_gsa, ",%02d",
306 satellite_satellitev[i].prn);
307 } else {
308 g_string_append_c(nmea_gsa, ',');
312 if (isnan(accuracy_pdop)) {
313 g_string_append_c(nmea_gsa, ',');
314 } else {
315 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_pdop);
318 if (isnan(accuracy_hdop)) {
319 g_string_append_c(nmea_gsa, ',');
320 } else {
321 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_hdop);
324 if (isnan(accuracy_vdop)) {
325 g_string_append_c(nmea_gsa, ',');
326 } else {
327 g_string_append_printf(nmea_gsa, ",%04.2f", accuracy_vdop);
330 add_nmea_checksum(nmea_gsa);
333 static void
334 update_nmea_gsv(void)
336 unsigned i;
337 unsigned sat_count;
338 unsigned sentence_count;
340 g_string_truncate(nmea_gsv[0], 0);
341 g_string_truncate(nmea_gsv[1], 0);
342 g_string_truncate(nmea_gsv[2], 0);
344 if (satellite_satellitec == 0) {
345 g_string_assign(nmea_gsv[0], "$GPGSV,1,1,00*79\r\n");
346 return;
349 sat_count = MIN(satellite_satellitec, 12);
350 sentence_count = ((sat_count - 1) / 4) + 1;
352 for (i = 0; i < sat_count; i++) {
353 int index_div, index_mod;
355 index_div = i / 4;
356 index_mod = i % 4;
358 if (index_mod == 0) {
359 g_string_append_printf(nmea_gsv[index_div],
360 "$GPGSV,%1d,%1d,%02d",
361 sentence_count,
362 index_div + 1,
363 sat_count);
366 g_string_append_printf(nmea_gsv[index_div],
367 ",%02u,%02u,%03u,%02u",
368 satellite_satellitev[i].prn,
369 satellite_satellitev[i].elevation,
370 satellite_satellitev[i].azimuth,
371 satellite_satellitev[i].snr);
374 for (i = 0; i < 3; i++) {
375 if (nmea_gsv[i]->len > 0) {
376 add_nmea_checksum(nmea_gsv[i]);
381 static void
382 update_nmea_rmc(void)
384 g_string_assign(nmea_rmc, "$GPRMC");
386 add_nmea_time(nmea_rmc);
388 if (get_fixstatus() > 1) {
389 g_string_append(nmea_rmc, ",A");
390 } else {
391 g_string_append(nmea_rmc, ",V");
394 add_nmea_latitude(nmea_rmc);
396 add_nmea_longitude(nmea_rmc);
398 if (isnan(course_speed)) {
399 g_string_append_c(nmea_rmc, ',');
400 } else {
401 g_string_append_printf(nmea_rmc, ",%07.3f",
402 course_speed / KNOTS);
405 if (isnan(course_direction)) {
406 g_string_append_c(nmea_rmc, ',');
407 } else {
408 g_string_append_printf(nmea_rmc, ",%06.2f",
409 course_direction);
412 if (time_seconds <= 0) {
413 g_string_append_c(nmea_rmc, ',');
414 } else {
415 struct tm *tm;
416 time_t seconds;
418 seconds = time_seconds;
419 tm = gmtime(&seconds);
421 g_string_append_printf(nmea_rmc, ",%02d%02d%02d",
422 tm->tm_mday, tm->tm_mon + 1, tm->tm_year % 100);
425 g_string_append(nmea_rmc, ",,");
427 add_nmea_checksum(nmea_rmc);
430 static void
431 update_nmea_gll(void)
433 g_string_assign(nmea_gll, "$GPGLL");
435 add_nmea_latitude(nmea_gll);
437 add_nmea_longitude(nmea_gll);
439 add_nmea_time(nmea_gll);
441 if (get_fixstatus() > 1) {
442 g_string_append(nmea_gll, ",A");
443 } else {
444 g_string_append(nmea_gll, ",V");
447 add_nmea_checksum(nmea_gll);
450 static void
451 update_nmea_vtg(void)
453 g_string_assign(nmea_vtg, "$GPVTG");
455 if (isnan(course_direction)) {
456 g_string_append(nmea_vtg, ",,");
457 } else {
458 g_string_append_printf(nmea_vtg, ",%04.2f,T", course_direction);
461 g_string_append(nmea_vtg, ",,"); /* FIXME */
463 if (isnan(course_speed)) {
464 g_string_append(nmea_vtg, ",,,,");
465 } else {
466 g_string_append_printf(nmea_vtg, ",%05.3f,N,%05.3f,K",
467 course_speed / KNOTS, course_speed);
470 add_nmea_checksum(nmea_vtg);
473 /******************************************************************************/
475 static gboolean
476 send_nmea_to_clients(gpointer data)
478 GString *string;
479 GList *l;
481 nmea_queued = FALSE;
483 string = g_string_sized_new(1024);
485 if (nmea_gga->len == 0) {
486 update_nmea_gga();
488 if (nmea_gsa->len == 0) {
489 update_nmea_gsa();
491 if (nmea_gsv[0]->len == 0) {
492 update_nmea_gsv();
494 if (nmea_rmc->len == 0) {
495 update_nmea_rmc();
497 if (nmea_gll->len == 0) {
498 update_nmea_gll();
500 if (nmea_vtg->len == 0) {
501 update_nmea_vtg();
504 g_string_append(string, nmea_gga->str);
505 g_string_append(string, nmea_gsa->str);
506 g_string_append(string, nmea_gsv[0]->str);
507 g_string_append(string, nmea_gsv[1]->str);
508 g_string_append(string, nmea_gsv[2]->str);
509 g_string_append(string, nmea_rmc->str);
510 g_string_append(string, nmea_gll->str);
511 g_string_append(string, nmea_vtg->str);
513 for (l = client_list; l; l = l->next) {
514 struct client *client = l->data;
516 if (client->raw) {
517 send_to_client(client, string->str, string->len);
521 g_string_free(string, TRUE);
523 return FALSE;
526 static void
527 queue_send_nmea_to_clients(void)
529 if (nmea_queued) {
530 return;
533 nmea_queued = TRUE;
535 g_idle_add(send_nmea_to_clients, NULL);
538 /******************************************************************************/
540 static void
541 add_sats(GString *string)
543 unsigned i, count;
545 if (satellite_satellitec == 0) {
546 g_string_append(string, ",Y=?");
547 return;
550 g_string_append(string, ",Y=-");
552 if (time_seconds <= 0) {
553 g_string_append(string, " ? ");
554 } else {
555 g_string_append_printf(string, " %d.000 ", time_seconds);
558 count = MIN(satellite_satellitec, 12);
560 g_string_append_printf(string, "%d:", count);
562 for (i = 0; i < count; i++) {
563 g_string_append_printf(string, "%d %d %d %d %d:",
564 satellite_satellitev[i].prn,
565 satellite_satellitev[i].elevation,
566 satellite_satellitev[i].azimuth,
567 satellite_satellitev[i].snr,
568 satellite_satellitev[i].used);
572 /******************************************************************************/
574 static gboolean
575 send_sats_to_clients(gpointer data)
577 GString *string;
578 GList *l;
580 sats_queued = FALSE;
582 string = g_string_sized_new(1024);
584 g_string_append(string, "GPSD");
586 add_sats(string);
588 g_string_append(string, "\r\n");
590 for (l = client_list; l; l = l->next) {
591 struct client *client = l->data;
593 if (client->watcher) {
594 send_to_client(client, string->str, string->len);
598 g_string_free(string, TRUE);
600 return FALSE;
603 static void
604 queue_send_sats_to_clients(void)
606 if (sats_queued) {
607 return;
610 sats_queued = TRUE;
612 g_idle_add(send_sats_to_clients, NULL);
615 /******************************************************************************/
617 static void
618 add_info(GString *string)
620 if (get_fixstatus() <= 1) {
621 g_string_append(string, ",O=?");
622 return;
625 g_string_append(string, ",O=-");
627 if (time_seconds <= 0) {
628 g_string_append(string, " ?");
629 } else {
630 g_string_append_printf(string, " %d.000", time_seconds);
633 g_string_append(string, " ?"); /* FIXME */
635 if (isnan(position_latitude)) {
636 g_string_append(string, " ?");
637 } else {
638 g_string_append_printf(string, " %.6f", position_latitude);
640 if (isnan(position_longitude)) {
641 g_string_append(string, " ?");
642 } else {
643 g_string_append_printf(string, " %.6f", position_longitude);
645 if (isnan(position_altitude)) {
646 g_string_append(string, " ?");
647 } else {
648 g_string_append_printf(string, " %.2f", position_altitude);
650 if (isnan(accuracy_hdop)) {
651 g_string_append(string, " ?");
652 } else {
653 g_string_append_printf(string, " %.2f", accuracy_hdop);
655 if (isnan(accuracy_vdop)) {
656 g_string_append(string, " ?");
657 } else {
658 g_string_append_printf(string, " %.2f", accuracy_vdop);
660 if (isnan(course_direction)) {
661 g_string_append(string, " ?");
662 } else {
663 g_string_append_printf(string, " %.4f", course_direction);
665 if (isnan(course_speed)) {
666 g_string_append(string, " ?");
667 } else {
668 g_string_append_printf(string, " %.3f",
669 course_speed / (KNOTS * 2));
671 if (isnan(course_climb)) {
672 g_string_append(string, " ?");
673 } else {
674 g_string_append_printf(string, " %.3f", course_climb);
677 g_string_append(string, " ? ? ?"); /* FIXME */
679 g_string_append_printf(string, " %d", get_fixstatus());
682 /******************************************************************************/
684 static gboolean
685 send_info_to_clients(gpointer data)
687 GString *string;
688 GList *l;
690 info_queued = FALSE;
692 string = g_string_sized_new(1024);
694 g_string_append(string, "GPSD");
696 add_info(string);
698 g_string_append(string, "\r\n");
700 for (l = client_list; l; l = l->next) {
701 struct client *client = l->data;
703 if (client->watcher) {
704 send_to_client(client, string->str, string->len);
708 g_string_free(string, TRUE);
710 return FALSE;
713 static void
714 queue_send_info_to_clients(void)
716 if (info_queued) {
717 return;
720 info_queued = TRUE;
722 g_idle_add(send_info_to_clients, NULL);
725 /******************************************************************************/
727 static gboolean
728 timeout_handler(gpointer data)
730 send_info_to_clients(NULL);
731 send_sats_to_clients(NULL);
732 send_nmea_to_clients(NULL);
734 return !!client_list;
737 /******************************************************************************/
739 static void
740 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE(
741 GClosure *closure,
742 GValue *return_value G_GNUC_UNUSED,
743 guint n_param_values,
744 const GValue *param_values,
745 gpointer invocation_hint G_GNUC_UNUSED,
746 gpointer marshal_data
749 typedef void (*GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE)(
750 gpointer data1,
751 gint arg_1,
752 gint arg_2,
753 gdouble arg_3,
754 gdouble arg_4,
755 gdouble arg_5,
756 gpointer data2);
758 GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE callback;
759 GCClosure *cc = (GCClosure*) closure;
760 gpointer data1, data2;
762 g_return_if_fail(n_param_values == 6);
764 if (G_CCLOSURE_SWAP_DATA(closure)) {
765 data1 = closure->data;
766 data2 = g_value_peek_pointer(param_values + 0);
767 } else {
768 data1 = g_value_peek_pointer(param_values + 0);
769 data2 = closure->data;
772 callback = (GMarshalFunc_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE) (
773 marshal_data ? marshal_data : cc->callback);
775 callback(data1,
776 g_value_get_int(param_values + 1),
777 g_value_get_int(param_values + 2),
778 g_value_get_double(param_values + 3),
779 g_value_get_double(param_values + 4),
780 g_value_get_double(param_values + 5),
781 data2);
784 static void
785 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE(
786 GClosure *closure,
787 GValue *return_value G_GNUC_UNUSED,
788 guint n_param_values,
789 const GValue *param_values,
790 gpointer invocation_hint G_GNUC_UNUSED,
791 gpointer marshal_data
794 typedef void (*GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE)(
795 gpointer data1,
796 gint arg_1,
797 gdouble arg_2,
798 gdouble arg_3,
799 gdouble arg_4,
800 gpointer data2);
802 GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE callback;
803 GCClosure *cc = (GCClosure*) closure;
804 gpointer data1, data2;
806 g_return_if_fail(n_param_values == 5);
808 if (G_CCLOSURE_SWAP_DATA(closure)) {
809 data1 = closure->data;
810 data2 = g_value_peek_pointer(param_values + 0);
811 } else {
812 data1 = g_value_peek_pointer(param_values + 0);
813 data2 = closure->data;
816 callback = (GMarshalFunc_VOID__INT_DOUBLE_DOUBLE_DOUBLE) (
817 marshal_data ? marshal_data : cc->callback);
819 callback(data1,
820 g_value_get_int(param_values + 1),
821 g_value_get_double(param_values + 2),
822 g_value_get_double(param_values + 3),
823 g_value_get_double(param_values + 4),
824 data2);
827 /******************************************************************************/
829 static void
830 connection_status_changed(
831 DBusGProxy *proxy,
832 gboolean connectionstatus,
833 gpointer user_data
836 device_connectionstatus = connectionstatus;
838 g_message("ConnectionStatusChanged(connectionstatus=%s)",
839 device_connectionstatus ? "TRUE" : "FALSE");
841 if ((client_list || nowait) && !device_connectionstatus) {
842 dbus_g_proxy_call_no_reply(proxy_usage, "RequestResource",
843 G_TYPE_STRING, "GPS",
844 G_TYPE_INVALID,
845 G_TYPE_INVALID);
849 static void
850 fix_status_changed(
851 DBusGProxy *proxy,
852 int fixstatus,
853 gpointer user_data
856 const char *str;
858 device_fixstatus = fixstatus;
860 switch (device_fixstatus) {
861 case 1: str = "NONE"; break;
862 case 2: str = "2D"; break;
863 case 3: str = "3D"; break;
864 default: str = "INVALID"; break;
867 g_message("FixStatusChanged(fixstatus=%s)", str);
869 g_string_truncate(nmea_gga, 0);
870 g_string_truncate(nmea_gsa, 0);
871 g_string_truncate(nmea_rmc, 0);
872 g_string_truncate(nmea_gll, 0);
874 queue_send_nmea_to_clients();
875 queue_send_info_to_clients();
878 static void
879 position_changed(
880 DBusGProxy *proxy,
881 int fields,
882 int timestamp,
883 double latitude,
884 double longitude,
885 double altitude,
886 gpointer user_data
889 position_timestamp = timestamp;
890 position_latitude = (fields & (1 << 0)) ? latitude : NAN;
891 position_longitude = (fields & (1 << 1)) ? longitude : NAN;
892 position_altitude = (fields & (1 << 2)) ? altitude : NAN;
894 g_debug("PositionChanged(timestamp=%d, latitude=%f, longitude=%f, altitude=%f)",
895 position_timestamp, position_latitude,
896 position_longitude, position_altitude);
898 g_string_truncate(nmea_gga, 0);
899 g_string_truncate(nmea_rmc, 0);
900 g_string_truncate(nmea_gll, 0);
902 queue_send_nmea_to_clients();
903 queue_send_info_to_clients();
906 static void
907 accuracy_changed(
908 DBusGProxy *proxy,
909 int fields,
910 double pdop,
911 double hdop,
912 double vdop,
913 gpointer user_data
916 accuracy_pdop = (fields & (1 << 0)) ? pdop : NAN;
917 accuracy_hdop = (fields & (1 << 1)) ? hdop : NAN;
918 accuracy_vdop = (fields & (1 << 2)) ? vdop : NAN;
920 g_debug("AccuracyChanged(pdop=%f, hdop=%f, vdop=%f)",
921 accuracy_pdop, accuracy_hdop, accuracy_vdop);
923 g_string_truncate(nmea_gga, 0);
924 g_string_truncate(nmea_gsa, 0);
926 queue_send_nmea_to_clients();
927 queue_send_info_to_clients();
930 static void
931 course_changed(
932 DBusGProxy *proxy,
933 int fields,
934 int timestamp,
935 double speed,
936 double direction,
937 double climb,
938 gpointer user_data
941 course_timestamp = timestamp;
942 course_speed = (fields & (1 << 0)) ? speed : NAN;
943 course_direction = (fields & (1 << 1)) ? direction : NAN;
944 course_climb = (fields & (1 << 2)) ? climb : NAN;
946 g_debug("CourseChanged(timestamp=%d, speed=%f, direction=%f, climb=%f)",
947 course_timestamp, course_speed,
948 course_direction, course_climb);
950 g_string_truncate(nmea_rmc, 0);
951 g_string_truncate(nmea_vtg, 0);
953 queue_send_nmea_to_clients();
954 queue_send_info_to_clients();
957 static void
958 satellites_changed(
959 DBusGProxy *proxy,
960 GPtrArray *satellites,
961 gpointer user_data
964 int i;
966 if (!satellites) {
967 satellite_satellitec = 0;
968 } else {
969 satellite_satellitec = MIN(SATELLITEC_MAX, satellites->len);
972 for (i = 0; i < satellite_satellitec; i++) {
973 struct satellite *sat;
974 GValueArray *val;
976 sat = satellite_satellitev + i;
977 val = satellites->pdata[i];
979 sat->prn = g_value_get_uint(g_value_array_get_nth(val, 0));
980 sat->used = g_value_get_boolean(g_value_array_get_nth(val, 1));
981 sat->elevation = g_value_get_uint(g_value_array_get_nth(val, 2));
982 sat->azimuth = g_value_get_uint(g_value_array_get_nth(val, 3));
983 sat->snr = g_value_get_uint(g_value_array_get_nth(val, 4));
985 g_debug("SatellitesChanged(index=%d, prn=%u, used=%s, elevation=%u, azimuth=%u, snr=%u)",
987 sat->prn,
988 sat->used ? "TRUE" : "FALSE",
989 sat->elevation,
990 sat->azimuth,
991 sat->snr);
994 g_string_truncate(nmea_gga, 0);
995 g_string_truncate(nmea_gsa, 0);
996 g_string_truncate(nmea_gsv[0], 0);
997 g_string_truncate(nmea_gsv[1], 0);
998 g_string_truncate(nmea_gsv[2], 0);
1000 queue_send_nmea_to_clients();
1001 queue_send_sats_to_clients();
1004 static void
1005 time_changed(
1006 DBusGProxy *proxy,
1007 int seconds,
1008 gpointer user_data
1011 time_seconds = seconds;
1013 g_debug("TimeChanged(seconds=%d)", time_seconds);
1015 g_string_truncate(nmea_gga, 0);
1016 g_string_truncate(nmea_rmc, 0);
1017 g_string_truncate(nmea_gll, 0);
1019 queue_send_nmea_to_clients();
1020 queue_send_info_to_clients();
1023 /******************************************************************************/
1025 static void
1026 start(void)
1028 dbus_g_proxy_connect_signal(proxy_device, "ConnectionStatusChanged",
1029 G_CALLBACK(connection_status_changed), NULL, NULL);
1030 dbus_g_proxy_connect_signal(proxy_device, "FixStatusChanged",
1031 G_CALLBACK(fix_status_changed), NULL, NULL);
1032 dbus_g_proxy_connect_signal(proxy_position, "PositionChanged",
1033 G_CALLBACK(position_changed), NULL, NULL);
1034 dbus_g_proxy_connect_signal(proxy_accuracy, "AccuracyChanged",
1035 G_CALLBACK(accuracy_changed), NULL, NULL);
1036 dbus_g_proxy_connect_signal(proxy_course, "CourseChanged",
1037 G_CALLBACK(course_changed), NULL, NULL);
1038 dbus_g_proxy_connect_signal(proxy_satellite, "SatellitesChanged",
1039 G_CALLBACK(satellites_changed), NULL, NULL);
1040 dbus_g_proxy_connect_signal(proxy_time, "TimeChanged",
1041 G_CALLBACK(time_changed), NULL, NULL);
1043 dbus_g_proxy_call_no_reply(proxy_usage, "RequestResource",
1044 G_TYPE_STRING, "GPS",
1045 G_TYPE_INVALID,
1046 G_TYPE_INVALID);
1049 gboolean connectionstatus = FALSE;
1051 dbus_g_proxy_call(proxy_device, "GetConnectionStatus", NULL,
1052 G_TYPE_INVALID,
1053 G_TYPE_BOOLEAN, &connectionstatus,
1054 G_TYPE_INVALID);
1055 connection_status_changed(NULL, connectionstatus, NULL);
1058 int fixstatus = 0;
1060 dbus_g_proxy_call(proxy_device, "GetFixStatus", NULL,
1061 G_TYPE_INVALID,
1062 G_TYPE_INT, &fixstatus,
1063 G_TYPE_INVALID);
1064 fix_status_changed(NULL, fixstatus, NULL);
1067 int fields = 0, timestamp = 0;
1068 double latitude = NAN, longitude = NAN, altitude = NAN;
1070 dbus_g_proxy_call(proxy_position, "GetPosition", NULL,
1071 G_TYPE_INVALID,
1072 G_TYPE_INT, &fields,
1073 G_TYPE_INT, &timestamp,
1074 G_TYPE_DOUBLE, &latitude,
1075 G_TYPE_DOUBLE, &longitude,
1076 G_TYPE_DOUBLE, &altitude,
1077 G_TYPE_INVALID);
1078 position_changed(NULL, fields, timestamp, latitude, longitude,
1079 altitude, NULL);
1082 int fields = 0;
1083 double pdop = NAN, hdop = NAN, vdop = NAN;
1085 dbus_g_proxy_call(proxy_accuracy, "GetAccuracy", NULL,
1086 G_TYPE_INVALID,
1087 G_TYPE_INT, &fields,
1088 G_TYPE_DOUBLE, &pdop,
1089 G_TYPE_DOUBLE, &hdop,
1090 G_TYPE_DOUBLE, &vdop,
1091 G_TYPE_INVALID);
1092 accuracy_changed(NULL, fields, pdop, hdop, vdop, NULL);
1095 int fields = 0, timestamp = 0;
1096 double speed = NAN, direction = NAN, climb = NAN;
1098 dbus_g_proxy_call(proxy_course, "GetCourse", NULL,
1099 G_TYPE_INVALID,
1100 G_TYPE_INT, &fields,
1101 G_TYPE_INT, &timestamp,
1102 G_TYPE_DOUBLE, &speed,
1103 G_TYPE_DOUBLE, &direction,
1104 G_TYPE_DOUBLE, &climb,
1105 G_TYPE_INVALID);
1106 course_changed(NULL, fields, timestamp, speed, direction,
1107 climb, NULL);
1110 GPtrArray *satellites = NULL;
1112 dbus_g_proxy_call(proxy_satellite, "GetSatellites", NULL,
1113 G_TYPE_INVALID,
1114 dbus_g_type_get_collection("GPtrArray",
1115 dbus_g_type_get_struct("GValueArray",
1116 G_TYPE_UINT,
1117 G_TYPE_BOOLEAN,
1118 G_TYPE_UINT,
1119 G_TYPE_UINT,
1120 G_TYPE_UINT,
1121 G_TYPE_INVALID)),
1122 &satellites,
1123 G_TYPE_INVALID);
1124 satellites_changed(NULL, satellites, NULL);
1127 int seconds;
1129 dbus_g_proxy_call(proxy_time, "GetTime", NULL,
1130 G_TYPE_INVALID,
1131 G_TYPE_INT, &seconds,
1132 G_TYPE_INVALID);
1133 time_changed(NULL, seconds, NULL);
1137 static void
1138 stop(void)
1140 dbus_g_proxy_call_no_reply(proxy_usage, "ReleaseResource",
1141 G_TYPE_STRING, "GPS",
1142 G_TYPE_INVALID,
1143 G_TYPE_INVALID);
1145 dbus_g_proxy_disconnect_signal(proxy_device, "ConnectionStatusChanged",
1146 G_CALLBACK(connection_status_changed), NULL);
1147 dbus_g_proxy_disconnect_signal(proxy_device, "FixStatusChanged",
1148 G_CALLBACK(fix_status_changed), NULL);
1149 dbus_g_proxy_disconnect_signal(proxy_position, "PositionChanged",
1150 G_CALLBACK(position_changed), NULL);
1151 dbus_g_proxy_disconnect_signal(proxy_accuracy, "AccuracyChanged",
1152 G_CALLBACK(accuracy_changed), NULL);
1153 dbus_g_proxy_disconnect_signal(proxy_course, "CourseChanged",
1154 G_CALLBACK(course_changed), NULL);
1155 dbus_g_proxy_disconnect_signal(proxy_satellite, "SatellitesChanged",
1156 G_CALLBACK(satellites_changed), NULL);
1157 dbus_g_proxy_disconnect_signal(proxy_time, "TimeChanged",
1158 G_CALLBACK(time_changed), NULL);
1161 /******************************************************************************/
1163 static void
1164 init_dbus(void)
1166 GError *error = NULL;
1168 connection = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
1170 if (!connection) {
1171 g_printerr("Failed to open connection to system bus: %s\n",
1172 error->message);
1173 g_error_free(error);
1174 exit(EXIT_FAILURE);
1177 dbus_g_object_register_marshaller(
1178 marshal_VOID__INT_DOUBLE_DOUBLE_DOUBLE,
1179 G_TYPE_NONE,
1180 G_TYPE_INT,
1181 G_TYPE_DOUBLE,
1182 G_TYPE_DOUBLE,
1183 G_TYPE_DOUBLE,
1184 G_TYPE_INVALID);
1185 dbus_g_object_register_marshaller(
1186 marshal_VOID__INT_INT_DOUBLE_DOUBLE_DOUBLE,
1187 G_TYPE_NONE,
1188 G_TYPE_INT,
1189 G_TYPE_INT,
1190 G_TYPE_DOUBLE,
1191 G_TYPE_DOUBLE,
1192 G_TYPE_DOUBLE,
1193 G_TYPE_INVALID);
1195 proxy_usage = dbus_g_proxy_new_for_name(connection, OUSAGED, USAGE,
1196 "org.freesmartphone.Usage");
1197 proxy_device = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1198 "org.freedesktop.Gypsy.Device");
1199 proxy_position = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1200 "org.freedesktop.Gypsy.Position");
1201 proxy_accuracy = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1202 "org.freedesktop.Gypsy.Accuracy");
1203 proxy_course = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1204 "org.freedesktop.Gypsy.Course");
1205 proxy_satellite = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1206 "org.freedesktop.Gypsy.Satellite");
1207 proxy_time = dbus_g_proxy_new_for_name(connection, OGPSD, GYPSY,
1208 "org.freedesktop.Gypsy.Time");
1210 dbus_g_proxy_add_signal(proxy_device, "ConnectionStatusChanged",
1211 G_TYPE_BOOLEAN,
1212 G_TYPE_INVALID);
1213 dbus_g_proxy_add_signal(proxy_device, "FixStatusChanged",
1214 G_TYPE_INT,
1215 G_TYPE_INVALID);
1216 dbus_g_proxy_add_signal(proxy_position, "PositionChanged",
1217 G_TYPE_INT,
1218 G_TYPE_INT,
1219 G_TYPE_DOUBLE,
1220 G_TYPE_DOUBLE,
1221 G_TYPE_DOUBLE,
1222 G_TYPE_INVALID);
1223 dbus_g_proxy_add_signal(proxy_accuracy, "AccuracyChanged",
1224 G_TYPE_INT,
1225 G_TYPE_DOUBLE,
1226 G_TYPE_DOUBLE,
1227 G_TYPE_DOUBLE,
1228 G_TYPE_INVALID);
1229 dbus_g_proxy_add_signal(proxy_course, "CourseChanged",
1230 G_TYPE_INT,
1231 G_TYPE_INT,
1232 G_TYPE_DOUBLE,
1233 G_TYPE_DOUBLE,
1234 G_TYPE_DOUBLE,
1235 G_TYPE_INVALID);
1236 dbus_g_proxy_add_signal(proxy_satellite, "SatellitesChanged",
1237 dbus_g_type_get_collection("GPtrArray",
1238 dbus_g_type_get_struct("GValueArray",
1239 G_TYPE_UINT,
1240 G_TYPE_BOOLEAN,
1241 G_TYPE_UINT,
1242 G_TYPE_UINT,
1243 G_TYPE_UINT,
1244 G_TYPE_INVALID)),
1245 G_TYPE_INVALID);
1246 dbus_g_proxy_add_signal(proxy_time, "TimeChanged",
1247 G_TYPE_INT,
1248 G_TYPE_INVALID);
1251 /******************************************************************************/
1253 static gboolean
1254 read_from_client(GIOChannel *source, GIOCondition condition, gpointer data)
1256 struct client *client;
1257 char buf[4096];
1258 gsize len = sizeof(buf);
1259 GIOStatus status;
1260 GString *reply;
1261 char *p;
1263 client = data;
1265 status = g_io_channel_read_chars(source, buf, len - 2, &len, NULL);
1267 if ((status == G_IO_STATUS_ERROR) || (status == G_IO_STATUS_EOF)) {
1268 remove_client(client);
1269 return FALSE;
1272 if (buf[len - 1] != '\n') {
1273 buf[len] = '\n';
1274 len++;
1277 buf[len] = '\0';
1279 g_message("Received command from client: %s", buf); /* FIXME */
1281 reply = g_string_sized_new(128);
1282 g_string_assign(reply, "GPSD");
1284 p = buf;
1286 while (*p) {
1287 switch (toupper(*p++)) {
1288 case 'A':
1289 if (isnan(position_altitude)) {
1290 g_string_append(reply, ",A=?");
1291 } else {
1292 g_string_append_printf(reply, ",A=%.3f",
1293 position_altitude);
1295 break;
1296 case 'B':
1297 g_string_append(reply, ",B=?");
1298 break;
1299 case 'C':
1300 g_string_append(reply, ",C=1.00"); /* FIXME */
1301 break;
1302 case 'D':
1303 if (time_seconds <= 0) {
1304 g_string_append(reply, ",D=?");
1305 } else {
1306 struct tm *tm;
1307 time_t seconds;
1309 seconds = time_seconds;
1310 tm = gmtime(&seconds);
1312 g_string_append_printf(reply,
1313 ",D=%04d-%02d-%02dT%02d:%02d:%02d.00Z",
1314 tm->tm_year + 1900, tm->tm_mon + 1,
1315 tm->tm_mday, tm->tm_hour, tm->tm_min,
1316 tm->tm_sec);
1318 break;
1319 case 'E':
1320 g_string_append(reply, ",E=");
1322 if (isnan(accuracy_pdop)) {
1323 g_string_append(reply, "?");
1324 } else {
1325 g_string_append_printf(reply, "%.2f",
1326 accuracy_pdop);
1328 if (isnan(accuracy_hdop)) {
1329 g_string_append(reply, " ?");
1330 } else {
1331 g_string_append_printf(reply, " %.2f",
1332 accuracy_hdop);
1334 if (isnan(accuracy_vdop)) {
1335 g_string_append(reply, " ?");
1336 } else {
1337 g_string_append_printf(reply, " %.2f",
1338 accuracy_vdop);
1340 break;
1341 case 'F':
1342 if (*p == '=') {
1343 while (isprint(*p) && !isspace(*p)) {
1344 p++;
1347 g_string_append(reply, ",F=gypsy");
1348 break;
1349 case 'G':
1350 if (*p == '=') {
1351 p += strcspn(p, ",\r\n");
1353 g_string_append(reply, ",G=GPS");
1354 break;
1355 case 'I':
1356 g_string_append(reply, ",I=gypsy");
1357 break;
1358 case 'J':
1359 if (*p == '=') {
1360 p++;
1362 if (*p == '1' || *p == '+') {
1363 p++;
1364 g_string_append(reply, ",J=1");
1365 } else {
1366 if (*p == '0' || *p == '-') {
1367 p++;
1369 g_string_append(reply, ",J=0");
1371 break;
1372 case 'K':
1373 g_string_append(reply, ",K=1 gypsy");
1374 break;
1375 case 'L':
1376 g_string_append(reply, ",L=3 " VERSION
1377 " abcdefgijklmnopqrstuvwxyz");
1378 break;
1379 case 'M':
1380 g_string_append_printf(reply, ",M=%d",
1381 get_fixstatus());
1382 break;
1383 case 'N':
1384 g_string_append(reply, ",N=0");
1385 break;
1386 case 'O':
1387 add_info(reply);
1388 break;
1389 case 'P':
1390 if (isnan(position_latitude)
1391 || isnan(position_longitude)) {
1392 g_string_append(reply, ",P=?");
1393 } else {
1394 g_string_append_printf(reply, ",P=%.6f %.6f",
1395 position_latitude, position_longitude);
1397 break;
1398 case 'Q':
1399 if (isnan(accuracy_pdop)
1400 && isnan(accuracy_hdop)
1401 && isnan(accuracy_vdop)) {
1402 g_string_append(reply, ",Q=?");
1403 } else {
1404 unsigned i, used;
1406 for (i = 0, used = 0; i < satellite_satellitec; i++) {
1407 if (satellite_satellitev[i].used) {
1408 used++;
1412 g_string_append_printf(reply,
1413 ",Q=%d %.2f %.2f %.2f %.2f %.2f", used,
1414 isnan(accuracy_pdop) ? 0.0 : accuracy_pdop,
1415 isnan(accuracy_hdop) ? 0.0 : accuracy_hdop,
1416 isnan(accuracy_vdop) ? 0.0 : accuracy_vdop,
1417 0.0, 0.0);
1419 break;
1420 case 'R':
1421 if (*p == '=') {
1422 p++;
1424 if (*p == '2' || *p == '1' || *p == '+') {
1425 client->raw = TRUE;
1426 p++;
1427 } else if (*p == '0' || *p == '-') {
1428 client->raw = FALSE;
1429 p++;
1430 } else {
1431 client->raw = !client->raw;
1433 g_string_append_printf(reply, ",R=%1d", client->raw);
1434 break;
1435 case 'S':
1436 g_string_append_printf(reply, ",S=%d",
1437 MAX(get_fixstatus() - 1, 0));
1438 break;
1439 case 'T':
1440 if (isnan(course_direction)) {
1441 g_string_append(reply, ",T=?");
1442 } else {
1443 g_string_append_printf(reply, ",T=%.4f",
1444 course_direction);
1446 break;
1447 case 'U':
1448 if (isnan(course_climb)) {
1449 g_string_append(reply, ",U=?");
1450 } else {
1451 g_string_append_printf(reply, ",U=%.3f",
1452 course_climb);
1454 break;
1455 case 'V':
1456 if (isnan(course_speed)) {
1457 g_string_append(reply, ",V=?");
1458 } else {
1459 g_string_append_printf(reply, ",V=%.3f",
1460 course_speed / (KNOTS * 2));
1462 break;
1463 case 'W':
1464 if (*p == '=') {
1465 p++;
1467 if (*p == '1' || *p == '+') {
1468 client->watcher = TRUE;
1469 p++;
1470 } else if (*p == '0' || *p == '-') {
1471 client->watcher = FALSE;
1472 p++;
1473 } else {
1474 client->watcher = !client->watcher;
1476 g_string_append_printf(reply, ",W=%1d", client->watcher);
1477 break;
1478 case 'X':
1479 if (time_seconds <= 0) {
1480 g_string_append(reply, ",X=?");
1481 } else {
1482 g_string_append_printf(reply, ",X=%d.000000",
1483 time_seconds);
1485 break;
1486 case 'Y':
1487 add_sats(reply);
1488 break;
1489 case 'Z':
1490 g_string_append(reply, ",Z=?"); /* FIXME */
1491 break;
1492 case '$':
1493 g_string_append(reply, ",$=?"); /* FIXME */
1494 break;
1495 case '\r':
1496 case '\n':
1497 goto finish;
1498 default:
1499 break;
1503 finish:
1504 g_string_append(reply, "\r\n");
1505 send_to_client(client, reply->str, reply->len);
1506 g_string_free(reply, TRUE);
1507 return TRUE;
1510 static gboolean
1511 accept_new_client(GIOChannel *source, GIOCondition condition, gpointer data)
1513 struct sockaddr_in sin;
1514 socklen_t len = sizeof(sin);
1515 struct client *client;
1516 int fd;
1518 g_message("New client request");
1520 fd = accept(g_io_channel_unix_get_fd(source),
1521 (struct sockaddr *) &sin, &len);
1523 if (fd < 0) {
1524 g_error("Cannot accept new connection: %s", strerror(errno));
1527 client = g_new0(struct client, 1);
1529 if (!client) {
1530 g_error("Cannot allocate memory");
1533 if (!client_list) {
1534 g_timeout_add_seconds(1, timeout_handler, NULL);
1537 if (!client_list && !nowait) {
1538 start();
1541 client_list = g_list_prepend(client_list, client);
1543 client->channel = g_io_channel_unix_new(fd);
1545 g_io_channel_set_flags(client->channel, G_IO_FLAG_NONBLOCK, NULL);
1546 g_io_channel_set_encoding(client->channel, NULL, NULL);
1547 g_io_channel_set_buffered(client->channel, FALSE);
1549 g_io_add_watch(client->channel,
1550 G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR,
1551 read_from_client, client);
1553 return TRUE;
1556 static void
1557 init_service(const char *service)
1559 struct servent *pse;
1560 struct sockaddr_in sin;
1561 GIOChannel *channel;
1562 int fd, one = 1;
1564 memset(&sin, 0, sizeof(sin));
1566 sin.sin_family = AF_INET;
1567 sin.sin_addr.s_addr = INADDR_ANY;
1569 if (!service) {
1570 service = getservbyname("gpsd", "tcp") ? "gpsd" : PORT;
1573 pse = getservbyname(service, "tcp");
1575 if (pse) {
1576 sin.sin_port = pse->s_port;
1577 } else {
1578 sin.sin_port = htons(atoi(service));
1581 if (!sin.sin_port) {
1582 g_error("Cannot get service entry: %s", service);
1585 fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
1587 if (fd < 0) {
1588 g_error("Cannot create socket: %s", strerror(errno));
1591 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) & one, sizeof(one));
1593 if (bind(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
1594 g_error("Cannot bind to port %s: %s", service, strerror(errno));
1597 if (listen(fd, 4) < 0) {
1598 g_error("Cannot listen on port %s: %s",
1599 service, strerror(errno));
1602 channel = g_io_channel_unix_new(fd);
1604 g_io_channel_set_encoding(channel, NULL, NULL);
1605 g_io_channel_set_buffered(channel, FALSE);
1607 g_io_add_watch(channel, G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR,
1608 accept_new_client, NULL);
1611 /******************************************************************************/
1613 static void
1614 print_to_syslog(const char *string)
1616 syslog(LOG_USER | LOG_INFO, "%s", string);
1619 static void
1620 log_to_syslog(
1621 const char *domain,
1622 GLogLevelFlags level,
1623 const char *message,
1624 gpointer user_data
1627 const char *prefix = NULL;
1628 GString *string;
1630 if ((level & G_LOG_LEVEL_MASK) == G_LOG_LEVEL_DEBUG) {
1631 return; /* FIXME */
1634 switch (level & G_LOG_LEVEL_MASK) {
1635 case G_LOG_LEVEL_ERROR: prefix = "ERROR: "; break;
1636 case G_LOG_LEVEL_CRITICAL: prefix = "CRITICAL: "; break;
1637 case G_LOG_LEVEL_WARNING: prefix = "WARNING: "; break;
1638 case G_LOG_LEVEL_MESSAGE: prefix = "MESSAGE: "; break;
1639 case G_LOG_LEVEL_INFO: prefix = "INFO: "; break;
1640 case G_LOG_LEVEL_DEBUG: prefix = "DEBUG: "; break;
1641 default: break;
1644 string = g_string_new(prefix);
1646 if (domain) {
1647 g_string_append_printf(string, "%s: ", domain);
1650 g_string_append(string, message);
1652 print_to_syslog(string->str);
1654 g_string_free(string, TRUE);
1657 /******************************************************************************/
1659 static void
1660 usage(void)
1662 printf("usage: "NAME" [-n] [-N] [-P pidfile] [-S port] [-h]\n\
1663 Options include:\n\
1664 -n = don't wait for client connects to poll GPS\n\
1665 -N = don't go into background\n\
1666 -P pidfile = set file to record process ID\n\
1667 -S integer (default "PORT") = set port for daemon\n\
1668 -h = help message\n\
1669 -V = emit version and exit.\n");
1672 /******************************************************************************/
1675 main(int argc, char **argv)
1677 GMainLoop *loop = NULL;
1678 gboolean background = TRUE;
1679 char *service = NULL;
1680 char *pid_file = NULL;
1682 g_set_prgname(NAME);
1684 opterr = 0;
1685 while (1) {
1686 int option;
1688 option = getopt(argc, argv, "F:D:S:bhNnP:VR:");
1690 if (option == -1) {
1691 break;
1694 switch (option) {
1695 case 'D':
1696 g_warning("Not implemented: -D");
1697 break;
1698 case 'F':
1699 g_warning("Not implemented: -F");
1700 break;
1701 case 'N':
1702 background = FALSE;
1703 break;
1704 case 'b':
1705 g_warning("Not implemented: -b");
1706 break;
1707 case 'R':
1708 g_warning("Not implemented: -R");
1709 break;
1710 case 'S':
1711 service = optarg;
1712 break;
1713 case 'n':
1714 nowait = TRUE;
1715 break;
1716 case 'P':
1717 pid_file = optarg;
1718 break;
1719 case 'V':
1720 printf(NAME" "VERSION"\n");
1721 exit(EXIT_SUCCESS);
1722 break;
1723 case 'h':
1724 case '?':
1725 default:
1726 usage();
1727 exit(EXIT_SUCCESS);
1728 break;
1732 if (background) {
1733 pid_t pid;
1734 int fd;
1736 pid = fork();
1738 if (pid < 0) {
1739 g_error("fork() failed");
1740 } else if (pid > 0) {
1741 exit(EXIT_SUCCESS);
1744 openlog(NAME, LOG_PID, LOG_USER);
1745 g_set_printerr_handler(print_to_syslog);
1746 g_log_set_default_handler(log_to_syslog, NULL);
1748 if (setsid() < 0) {
1749 g_error("setsid() failed");
1752 chdir("/");
1754 fd = open("/dev/null", O_RDWR, 0);
1756 if (fd < 0) {
1757 g_error("Cannot open /dev/null");
1760 dup2(fd, STDIN_FILENO);
1761 dup2(fd, STDOUT_FILENO);
1762 dup2(fd, STDERR_FILENO);
1764 close(fd);
1767 if (pid_file) {
1768 FILE *fp;
1770 fp = fopen(pid_file, "w");
1772 if (!fp) {
1773 g_error("Cannot create PID file: %s", pid_file);
1776 fprintf(fp, "%u\n", (unsigned) getpid());
1777 fclose(fp);
1780 g_type_init();
1782 loop = g_main_loop_new(NULL, FALSE);
1784 nmea_gga = g_string_new(NULL);
1785 nmea_gsa = g_string_new(NULL);
1786 nmea_gsv[0] = g_string_new(NULL);
1787 nmea_gsv[1] = g_string_new(NULL);
1788 nmea_gsv[2] = g_string_new(NULL);
1789 nmea_rmc = g_string_new(NULL);
1790 nmea_gll = g_string_new(NULL);
1791 nmea_vtg = g_string_new(NULL);
1793 init_service(service);
1795 init_dbus();
1797 if (nowait) {
1798 start();
1801 g_main_loop_run(loop);
1803 return 0;