#include <errno.h>
#include <sys/wait.h>
#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>
+#endif
#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);
static gboolean gps_channel_cb_input(GIOChannel *src, GIOCondition condition, gpointer data);
static gboolean gps_channel_cb_connect(GIOChannel * src, GIOCondition condition, gpointer data);
static gboolean gps_connect_socket(Gps *gps);
-static gboolean gps_connect_file(Gps *gps);
+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
gps->io.address=NULL;
gps->io.type=type;
gps->io.conn=RCVR_OFF;
-gps->data.lat=64.20;
+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;
}
gpsdata->vel_offsety=-(gint)(floor(gpsdata->speed*cos(tmp)+0.5f));
}
+#ifdef WITH_HILDON_DBUS_BT
+void
+gps_connect_response(DBusGProxy *proxy, DBusGProxyCall *call_id, Gps *gps)
+{
+GError *error = NULL;
+gchar *fdpath = NULL;
+
+if (gps->io.conn==RCVR_DOWN && gps->io.address) {
+ if (!dbus_g_proxy_end_call(gps->io.rfcomm_req_proxy, call_id, &error, G_TYPE_STRING, &fdpath, G_TYPE_INVALID)) {
+ if (error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) {
+ /* If we're already connected, it's not an error, unless
+ * they don't give us the file descriptor path, in which
+ * case we re-connect.*/
+ if (!strcmp(BTCOND_ERROR_CONNECTED, dbus_g_error_get_name(error)) || !fdpath) {
+ g_printerr("Caught remote method exception %s: %s", dbus_g_error_get_name(error), error->message);
+ gps_disconnect(gps);
+
+ if (gps->connection_retry==NULL)
+ return;
+
+ if (gps->connection_retry(gps, error->message)==TRUE) {
+ gps_connect_later(gps);
+ } else {
+ gps_conn_set_state(gps, RCVR_OFF);
+ }
+ return;
+ }
+ } else {
+ /* Unknown error. */
+ g_printerr("Error: %s\n", error->message);
+ gps_disconnect(gps);
+ gps_connect_later(gps); /* Try again later. */
+ return;
+ }
+ }
+ gps_connect_file(gps, fdpath);
+}
+/* else { Looks like the middle of a disconnect. Do nothing. } */
+}
+#endif
+
/**
* Place a request to connect about 1 second after the function is called.
*/
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);
}
{
switch (gps->io.type) {
case GPS_IO_FILE:
- return gps_connect_file(gps);
+ return TRUE;
break;
case GPS_IO_RFCOMM:
case GPS_IO_TCP:
* Helper to open a file or device node
*/
static gboolean
-gps_connect_file(Gps *gps)
+gps_connect_file(Gps *gps, gchar *file)
{
-if (-1==(gps->io.fd=open(gps->io.address, O_RDONLY))) {
+if (-1==(gps->io.fd=open(file, O_RDONLY))) {
gps_disconnect(gps);
gps_connect_later(gps);
+ /* Add retry cb */
return FALSE;
}
return TRUE;
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) {
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);
#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);
{
g_assert(gps);
+g_debug("GPS: Disconnecting from gps");
/* Remove watches. */
if (gps->io.clater_sid) {
g_source_remove(gps->io.clater_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;
/* 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;
}
#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");
#ifdef WITH_HILDON_DBUS_BT
if (gps->io.type==GPS_IO_HILDON_DBUS && gps->io.rfcomm_req_proxy) {
- GError *error = NULL;
+ GError *error = NULL;
- 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);
+ 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);
}
#endif
#ifdef WITH_BLUEZ_DBUS_BT
if (gps->io.type==GPS_IO_BLUEZ_DBUS && gps->io.rfcomm_req_proxy) {
GError *error = NULL;
-
}
#endif
}
+static void
+gps_simulate_start(Gps *gps)
+{
+gps->data.lat=60.45;
+gps->data.lon=22.26;
+gps->io.conn=RCVR_FIXED;
+gps->data.fix=FIX_2D;
+gps_data_integerize(&gps->data);
+}
+
static gboolean
-gps_simulate(Gps *gps)
+gps_simulate_move(Gps *gps)
{
+static gdouble slat=0, slon=0;
+gdouble plat, plon;
+gfloat h;
g_assert(gps);
-gps->data.lat+=g_random_double_range(-0.0002,0.0002);
-gps->data.lon+=g_random_double_range(-0.0002,0.0002);
+if (g_random_double()<0.5) {
+ 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;
+gps->data.lat+=slat;
+gps->data.lon+=slon;
BOUND(gps->data.lat, -80.0, 80.0);
BOUND(gps->data.lon, -80.0, 80.0);
-gps->data.speed=1+g_random_double_range(0.0,5.0);
-gps->data.heading+=g_random_double_range(-2.0,2.0);
+
+g_debug("Sim: %f %f\n", gps->data.lat, gps->data.lon);
+
+gps->data.speed=1+g_random_double_range(0.1, 10.0);
+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, FALSE);
+if (gps->update_location!=NULL) {
+ gps->update_location(gps);
+}
gps->data.lheading=gps->data.heading;
-map_refresh_mark();
-return TRUE;
+return gps->io.conn==RCVR_FIXED ? TRUE : FALSE;
}
/**
* 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;
}
gboolean
gps_connect_now(Gps *gps)
{
+GError *error = NULL;
+
g_assert(gps);
g_debug("GPS: Connecting GPS type %d to %s:%d\n", gps->io.type, gps->io.address, gps->io.port);
case GPS_IO_FILE:
if (!gps->io.address)
return FALSE;
- if (*gps->io.address=='/')
- gps->io.fd=open(gps->io.address, O_RDONLY);
- else
- return FALSE;
+ gps_connect_file(gps, gps->io.address);
+ return FALSE;
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;
+ }
+ if (NULL == (gps->io.rfcomm_req_proxy = dbus_g_proxy_new_for_name(gps->io.dbus_conn, BTCOND_SERVICE, BTCOND_REQ_PATH, BTCOND_REQ_INTERFACE))) {
+ g_printerr("Failed to open connection to %s.\n", BTCOND_REQ_INTERFACE);
+ return FALSE;
+ }
+
if (gps->io.rfcomm_req_proxy) {
gboolean mybool = TRUE;
- dbus_g_proxy_begin_call(_rfcomm_req_proxy,
- BTCOND_RFCOMM_CONNECT_REQ, (DBusGProxyCallNotify)gps_connect_response, NULL, NULL,
+ dbus_g_proxy_begin_call(gps->io.rfcomm_req_proxy,
+ BTCOND_RFCOMM_CONNECT_REQ, (DBusGProxyCallNotify)gps_connect_response, gps, NULL,
G_TYPE_STRING, gps->io.address,
G_TYPE_STRING, "SPP",
G_TYPE_BOOLEAN, &mybool,
G_TYPE_INVALID);
}
-#else
- return FALSE;
-#endif
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
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 */
- gps->data.lat=64.22;
- gps->data.lat=22.22;
- gps->io.conn=RCVR_FIXED;
- gps->data.fix=FIX_2D;
- gps_data_integerize(&gps->data);
- gps->io.input_sid=g_timeout_add(1000, (GSourceFunc)gps_simulate, gps);
+ gps_simulate_start(gps);
+ gps->io.input_sid=g_timeout_add(1000, (GSourceFunc)gps_simulate_move, gps);
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;
}
* 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;
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;
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;
}
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);
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: