]> err.no Git - mapper/blobdiff - src/gps.c
Add header and cast properly.
[mapper] / src / gps.c
index 3ef0ebf822700e7ae6c72d8db8365febfa65c7aa..1962f5deef4d126a52a61d7702d68f7a365c9728 100644 (file)
--- a/src/gps.c
+++ b/src/gps.c
 #include <glib/gstdio.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
 #include <fcntl.h>
 
 #include "gps.h"
 #include "latlon.h"
 #include "map.h"
-#include "gps-nmea-parse.h"
 #include "gps-conn.h"
-#include "track.h"
+#include "gps-nmea-parse.h"
 
 #ifdef WITH_HILDON_DBUS_BT
 #include <bt-dbus.h>
@@ -53,7 +55,7 @@
 
 #ifdef WITH_GPSBT
 #include <gpsbt.h>
-static gpsbt_t ctx = {0};
+static gpsbt_t ctx;
 #endif
 
 static gboolean gps_channel_cb_error(GIOChannel *src, GIOCondition condition, gpointer data);
@@ -64,6 +66,34 @@ static gboolean gps_connect_file(Gps *gps, gchar *file);
 
 #define GPSD_NMEA "r+\r\n"
 #define GPSD_PORT (2947)
+#define LOCALHOST "127.0.0.1"
+#define GPS_DEVICE_FILE "/dev/ttyS0"
+
+const GpsTypes gps_types[] = {
+#ifdef WITH_BLUEZ
+       { GPS_IO_RFCOMM,                "Bluetooth connection (old)", TRUE, TRUE, FALSE},
+#endif
+#ifdef WITH_HILDON_DBUS_BT
+       { GPS_IO_HILDON_DBUS,   "Bluetooth connection (btconn)", TRUE, TRUE, FALSE },
+#endif
+#ifdef WITH_BLUEZ_DBUS_BT
+       { GPS_IO_BLUEZ_DBUS,    "Bluetooth connection (new)", TRUE, TRUE, FALSE},
+#endif
+       { GPS_IO_GPSD,                  "GPSD Connection", FALSE, TRUE, TRUE, GPSD_PORT, LOCALHOST },
+       { GPS_IO_FILE,                  "File or device", FALSE, TRUE, FALSE, 0, GPS_DEVICE_FILE },
+       { GPS_IO_TCP,                   "TCP Connection", FALSE, TRUE, TRUE, 1024, LOCALHOST },
+#ifdef WITH_GYPSY
+       { GPS_IO_GYPSY,                 "Gypsy", FALSE, TRUE, FALSE },
+#endif
+       { GPS_IO_SIMULATION,    "Simulation (random)", FALSE, FALSE, FALSE},
+       { 0 }
+};
+
+const GpsTypes *
+gps_get_supported_types(void) 
+{
+return gps_types;
+}
 
 /**
  * Init GPS structure
@@ -79,10 +109,16 @@ gps->io.fd=-1;
 gps->io.address=NULL;
 gps->io.type=type;
 gps->io.conn=RCVR_OFF;
+gps->io.nmea=NULL;
+gps->io.nmea_cnt=0;
 gps->data.lat=60.20;
 gps->data.lon=22.20;
 gps->connection_error=NULL;
 gps->connection_retry=NULL;
+gps->connection_progress=NULL;
+gps->update_location=NULL;
+gps->update_info=NULL;
+gps->update_satellite=NULL;
 gps_data_integerize(&gps->data);
 return gps;
 }
@@ -157,12 +193,12 @@ if (gps->io.conn==RCVR_DOWN && gps->io.address) {
                } else {
                        /* Unknown error. */
                        g_printerr("Error: %s\n", error->message);
-                       gps_disconnect(_gps);
-                       gps_connect_later(_gps);        /* Try again later. */
+                       gps_disconnect(gps);
+                       gps_connect_later(gps); /* Try again later. */
                        return;
                }
        }
-       gps_connect_fd(fdpath);
+       gps_connect_file(gps, fdpath);
 }
 /* else { Looks like the middle of a disconnect.  Do nothing. } */
 }
@@ -175,7 +211,7 @@ void
 gps_connect_later(Gps *gps)
 {
 g_assert(gps);
-g_assert(gps->io.clater_sid!=0);
+g_assert(gps->io.clater_sid==0);
 gps->io.clater_sid=g_timeout_add(1000, (GSourceFunc)gps_connect_now, gps);
 }
 
@@ -223,20 +259,25 @@ static gboolean
 gps_connect_socket(Gps *gps)
 {
 gint r, e;
+g_assert(gps);
+
 switch (gps->io.type) {
-#ifdef WITH_BLUEZ
        case GPS_IO_RFCOMM:
+#ifdef WITH_BLUEZ
+               g_debug("RFCOMM: %d", gps->io.fd);
                r=connect(gps->io.fd, (struct sockaddr *)&gps->io.rcvr_addr_rc, sizeof(gps->io.rcvr_addr_rc));
-       break;
 #endif
+       break;
        case GPS_IO_TCP:
        case GPS_IO_GPSD:
+               g_debug("TCP: %d", gps->io.fd);
                r=connect(gps->io.fd, (struct sockaddr *)&gps->io.rcvr_addr_in, sizeof(gps->io.rcvr_addr_in));
        break;
        default:
                return FALSE;
 }
 e=errno;
+g_debug("GPS: Error %d (%s)", e, strerror(e));
 
 /* The socket is non blocking so handle it */
 if (r != 0) {
@@ -245,12 +286,12 @@ if (r != 0) {
        case EBUSY:
        case EINPROGRESS:
        case EALREADY:
-               g_printf("*** Connection in progress... %d %d\n", e, r);
+               g_printerr("*** Connection in progress... %d %d\n", e, r);
                perror("INFO: ");
                return TRUE;
        break;
        case EHOSTUNREACH:
-               g_printf("*** Bluetooth/GPS device not found.\n");
+               g_printerr("*** Bluetooth/GPS device not found.\n");
                gps_disconnect(gps);
 #if 0
         set_action_activate("gps_enable", FALSE);
@@ -258,9 +299,14 @@ if (r != 0) {
 #endif
                return FALSE;
        break;
+       case ECONNREFUSED:
+               g_printerr("*** Connection refused.\n");
+               gps_disconnect(gps);
+               return FALSE;
+       break;
        default:
                /* Connection failed.  Disconnect and try again later. */
-               g_printf("### Connect failed, retrying... %d %d\n", e, r);
+               g_printerr("### Connect failed, retrying... %d %d\n", e, r);
                perror("ERROR: ");
                gps_disconnect(gps);
                gps_connect_later(gps);
@@ -283,6 +329,7 @@ gps_disconnect(Gps *gps)
 {
 g_assert(gps);
 
+g_debug("GPS: Disconnecting from gps");
 /* Remove watches. */
 if (gps->io.clater_sid) {
        g_source_remove(gps->io.clater_sid);
@@ -303,6 +350,7 @@ if (gps->io.input_sid) {
 
 /* Destroy the GIOChannel object. */
 if (gps->io.channel) {
+       g_debug("GPS: Shutting down IO channel");
        g_io_channel_shutdown(gps->io.channel, FALSE, NULL);
        g_io_channel_unref(gps->io.channel);
        gps->io.channel=NULL;
@@ -310,6 +358,7 @@ if (gps->io.channel) {
 
 /* Close the file descriptor. */
 if (gps->io.fd!=-1) {
+       g_debug("GPS: Closing fd %d", gps->io.fd);
        close(gps->io.fd);
        gps->io.fd=-1;
 }
@@ -317,6 +366,8 @@ if (gps->io.fd!=-1) {
 #ifdef WITH_GPSBT
 if (gps->io.type==GPS_IO_GPSD) {
        gint r;
+
+       g_debug("GPS: Requesting gpsd shutdown using gpsbt");
        r=gpsbt_stop(&ctx);
        if (r!=0)
                g_warning("Failed to stop gpsd\n");
@@ -327,6 +378,7 @@ if (gps->io.type==GPS_IO_GPSD) {
 if (gps->io.type==GPS_IO_HILDON_DBUS && gps->io.rfcomm_req_proxy) {
        GError *error = NULL;
 
+       g_debug("GPS: Requesting rfcomm disconnection");
        dbus_g_proxy_call(gps->io.rfcomm_req_proxy, BTCOND_RFCOMM_CANCEL_CONNECT_REQ, &error, G_TYPE_STRING, gps->io.address, G_TYPE_STRING, "SPP", G_TYPE_INVALID, G_TYPE_INVALID);
        error = NULL;
        dbus_g_proxy_call(gps->io.rfcomm_req_proxy, BTCOND_RFCOMM_DISCONNECT_REQ, &error,G_TYPE_STRING, gps->io.address, G_TYPE_STRING, "SPP", G_TYPE_INVALID, G_TYPE_INVALID);
@@ -361,8 +413,8 @@ gfloat h;
 g_assert(gps);
 
 if (g_random_double()<0.5) {
-       slat=g_random_double_range(-0.0003, 0.0003);
-       slon=g_random_double_range(-0.0003, 0.0003);
+       slat=g_random_double_range(-0.0008, 0.0008);
+       slon=g_random_double_range(-0.0008, 0.0008);
 }
 plat=gps->data.lat;
 plon=gps->data.lon;
@@ -378,9 +430,10 @@ h=calculate_course(plat, plon, gps->data.lat, gps->data.lon);
 gps->data.heading=(h<0) ? 360+h : h;
 gps->data.time=time(NULL);
 gps_data_integerize(&gps->data);
-track_add(&gps->data, FALSE);
+if (gps->update_location!=NULL) {
+       gps->update_location(gps);
+}
 gps->data.lheading=gps->data.heading;
-map_refresh_mark();
 
 return gps->io.conn==RCVR_FIXED ? TRUE : FALSE;
 }
@@ -400,13 +453,15 @@ gps->io.last=gps->io.buffer+GPS_READ_BUF_SIZE-1;
  * Connect file descriptor to channel and add watches. 
  */
 static void
-gps_read_channel_connect(Gps *gps)
+gps_connect_channel_connect(Gps *gps)
 {
+g_assert(gps->io.channel==NULL);
 gps->io.channel=g_io_channel_unix_new(gps->io.fd);
 g_io_channel_set_encoding(gps->io.channel, NULL, NULL);
 g_io_channel_set_flags(gps->io.channel, G_IO_FLAG_NONBLOCK, NULL);
-gps->io.error_sid=g_io_add_watch_full(gps->io.channel, G_PRIORITY_HIGH_IDLE, G_IO_ERR | G_IO_HUP, gps_channel_cb_error, NULL, NULL);
-gps->io.connect_sid=g_io_add_watch_full(gps->io.channel, G_PRIORITY_HIGH_IDLE, G_IO_OUT, gps_channel_cb_connect, NULL, NULL);
+g_io_channel_set_buffered(gps->io.channel, FALSE);
+gps->io.error_sid=g_io_add_watch_full(gps->io.channel, G_PRIORITY_HIGH_IDLE, G_IO_ERR | G_IO_HUP | G_IO_NVAL, gps_channel_cb_error, gps, NULL);
+gps->io.connect_sid=g_io_add_watch_full(gps->io.channel, G_PRIORITY_HIGH_IDLE, G_IO_OUT, gps_channel_cb_connect, gps, NULL);
 gps->io.clater_sid=0;
 }
 
@@ -436,33 +491,47 @@ switch (gps->io.type) {
        break;
        case GPS_IO_TCP:
        case GPS_IO_GPSD:
-               if (gps->io.conn==RCVR_DOWN && gps->io.address) {
+               if (gps->io.conn<=RCVR_DOWN && gps->io.address) {
                        gps->io.fd=socket(AF_INET, SOCK_STREAM, 0);
                        if (gps->io.fd==-1) {
                                g_debug("Failed to create socket\n");
                                gps_connect_later(gps);
                                return FALSE;
                        }
-                       memcpy(&gps->io.rcvr_addr_in.sin_addr.s_addr, gps->io.address, strlen(gps->io.address));
+                       g_debug("TCP: Preparing to connect to %s:%d", gps->io.address, gps->io.port);
                        gps->io.rcvr_addr_in.sin_family = AF_INET;
                        gps->io.rcvr_addr_in.sin_port = htons(gps->io.port);
+                       if (inet_pton(AF_INET, gps->io.address, &gps->io.rcvr_addr_in.sin_addr.s_addr)<1) {
+                               perror("TCP:");
+                               return FALSE;
+                       }
                } else {
                        return FALSE;
                }
 #ifdef WITH_GPSBT
                if (gps->io.type==GPS_IO_GPSD) {
-                       gint r;
-                       gchar ebuf[128];
-                       r=gpsbt_start(NULL, 0, 0, 0, ebuf, 128, 0, &ctx);
-                       if (r!=0) {
-                               g_warning("Failed to start gpsd");
+                       gchar errbuf[255];
+
+                       /* Force correct gpsd binary */
+                       setenv ("GPSD_PROG", "/usr/sbin/gpsd", 1);
+
+                       memset(&ctx, 0, sizeof(ctx))
+                       memset(&errbuf, 0, sizeof(errbuf))
+                       errno=0;
+
+                       if (gpsbt_start(NULL, 0, 0, 0, errbuf, sizeof(errbuf), 0, &ctx)!=0)
+                               g_warning("Failed to start gpsd: %d (%s) %s\n", errno, strerror(errno), errbuff);
                                return FALSE;
+                       } else {
+                               g_debug("Waiting for gpsd");
+                               sleep(2);
+                               g_debug("Done wating");
                        }
                }
 #endif
        break;
-       case GPS_IO_HILDON_DBUS:
 #ifdef WITH_HILDON_DBUS_BT
+       case GPS_IO_HILDON_DBUS:
                if (NULL == (gps->io.dbus_conn=dbus_g_bus_get(DBUS_BUS_SYSTEM, &error))) {
                        g_printerr("Failed to get D-Bus connection.");
                        return FALSE;
@@ -481,15 +550,14 @@ switch (gps->io.type) {
                                G_TYPE_BOOLEAN, &mybool,
                                G_TYPE_INVALID);
                }
-#endif
-               return FALSE;
        break;
-       case GPS_IO_RFCOMM:
+#endif
 #ifdef WITH_BLUEZ
+       case GPS_IO_RFCOMM:
                if (!gps->io.address)
                        return FALSE;
 
-               if (gps->io.conn==RCVR_DOWN && gps->io.address) {
+               if (gps->io.conn<=RCVR_DOWN && gps->io.address) {
                        gps->io.fd=socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
 
                        /* If file descriptor creation failed, try again later.  Note that
@@ -506,7 +574,6 @@ switch (gps->io.type) {
                        str2ba(gps->io.address, &gps->io.rcvr_addr_rc.rc_bdaddr);
                }
 #endif
-               return FALSE;
        break;
        case GPS_IO_SIMULATION:
                /* Set a periodic cb to generate random movement */
@@ -515,12 +582,13 @@ switch (gps->io.type) {
                return FALSE;
        break;
        default:
+               g_printerr("Unknown GPS connection type\n");
                return FALSE;
        break;
 }
 
 gps_read_buffer_prepare(gps);
-gps_read_channel_connect(gps);
+gps_connect_channel_connect(gps);
 gps_connect(gps);
 return FALSE;
 }
@@ -529,12 +597,13 @@ return FALSE;
  * Connection callback
  */
 static gboolean
-gps_channel_cb_connect(GIOChannel * src, GIOCondition condition, gpointer data)
+gps_channel_cb_connect(GIOChannel *src, GIOCondition condition, gpointer data)
 {
 gint error, size = sizeof(error);
 Gps *gps=(Gps *)data;
 
 g_assert(data);
+g_assert(gps->io.channel);
 
 gps->io.curr=gps->io.buffer;
 gps->io.last=gps->io.buffer+GPS_READ_BUF_SIZE-1;
@@ -555,9 +624,15 @@ switch (gps->io.type) {
                if (gps->io.type==GPS_IO_GPSD) {
                        GIOStatus status;
                        gsize written;
-                       status=g_io_channel_write_chars(gps->io.channel, GPSD_NMEA, -1, &written, NULL);
-                       if (status!=G_IO_STATUS_NORMAL || written!=sizeof(GPSD_NMEA)) {
-                               g_printerr("Failed to set gpsd to NMEA mode\n");
+                       GError *error = NULL;
+
+                       g_debug("GPS: Requesting NMEA mode from GPSD using cmd %s", GPSD_NMEA);
+                       status=g_io_channel_write_chars(gps->io.channel, GPSD_NMEA, strlen(GPSD_NMEA), &written, &error);
+                       if (status==G_IO_STATUS_NORMAL && written==strlen(GPSD_NMEA)) {
+                               g_debug("GPS: NMEA mode set (%d %d)", (gint)written, (gint)status);
+                       } else {
+                               g_printerr("Failed to set gpsd to NMEA mode: %d %d %s\n",(gint)status,(gint)written, error->message);
+                               g_error_free(error);
                                gps_disconnect(gps);
                                gps_connect_later(gps);
                                gps->io.connect_sid=0;
@@ -588,15 +663,20 @@ gps_channel_cb_error(GIOChannel *src, GIOCondition condition, gpointer data)
 Gps *gps=(Gps *)data;
 
 g_assert(data);
+g_debug("GPS: Channel error %d", condition);
+
 /* An error has occurred - re-connect(). */
 gps_disconnect(gps);
-track_add(NULL, FALSE);
-/* _speed_excess = FALSE; */
+
+/* Do error cb */
+if (gps->connection_error!=NULL)
+       gps->connection_error(gps, "GPS Connection error");
 
 if (gps->io.conn > RCVR_OFF) {
        gps_conn_set_state(gps, RCVR_DOWN);
        gps_connect_later(gps);
 }
+
 return FALSE;
 }
 
@@ -633,8 +713,13 @@ switch (status) {
                                if (*sptr)
                                        *sptr = '\0';   /* take checksum out of the buffer. */
 
-                               data=g_strdup(gps->io.buffer);
-                               g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)gps_nmea_parse, data, NULL);
+                               gps->io.nmea_cnt++;
+                               gps->io.nmea=gps->io.buffer;
+
+                               g_assert(gps->io.nmea);
+                               g_debug("NMEA1 %d: (%s)", gps->io.nmea_cnt, gps->io.nmea);
+
+                               gps_nmea_parse(gps);
                        } else {
                                /* There was a checksum, and it was bad. */
                                g_printerr("%s: Bad checksum in NMEA sentence:\n%s\n", __PRETTY_FUNCTION__, gps->io.buffer);
@@ -653,15 +738,34 @@ switch (status) {
                        gps->io.curr -= (eol - gps->io.buffer + 1);
                }
        }
+       gps->io.errors=0;
+       gps->io.again=0;
        break;
        case G_IO_STATUS_ERROR:
        case G_IO_STATUS_EOF:
                gps_disconnect(gps);
                gps_connect_later(gps);
                gps->io.errors++;
+               if (gps->io.errors>10) {
+                       if (gps->connection_error==NULL)
+                               return FALSE;
+                       else {
+                               gps->connection_error(gps, "GPS data read error.");                     
+                       }
+               }
                return FALSE;
        break;
        case G_IO_STATUS_AGAIN:
+               gps->io.again++;
+               if (gps->io.again>20) {
+                       gps_disconnect(gps);
+                       gps_connect_later(gps);
+                       if (gps->connection_error==NULL)
+                               return FALSE;
+                       else {
+                               gps->connection_error(gps, "GPS connection deadlock.");
+                       }
+               }
                return TRUE;
        break;
        default: