2 * Navit, a modular navigation system.
3 * Copyright (C) 2005-2008 Navit Team
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
37 static struct vehicle_priv {
42 struct callback_list *cbl;
44 struct event_watch *evwatch;
46 struct gps_data_t *gps;
68 #define DEFAULT_RETRY_INTERVAL 10 // seconds
69 #define MIN_RETRY_INTERVAL 1 // seconds
71 static void vehicle_gpsd_io(struct vehicle_priv *priv);
75 vehicle_gpsd_callback(struct gps_data_t *data, char *buf, size_t len)
77 vehicle_gpsd_callback(struct gps_data_t *data, char *buf, size_t len,
81 char *pos,*nmea_data_buf;
82 int i=0,sats_signal=0;
84 struct vehicle_priv *priv = vehicle_last;
85 if( len > 0 && buf[0] == '$' ) {
88 memcpy(buffer, buf, len);
89 pos=strchr(buffer,'\n');
92 if (!priv->nmea_data_buf || strlen(priv->nmea_data_buf) < 65536) {
93 nmea_data_buf=g_strconcat(priv->nmea_data_buf ? priv->nmea_data_buf : "", buffer, NULL);
94 g_free(priv->nmea_data_buf);
95 priv->nmea_data_buf=nmea_data_buf;
97 dbg(0, "nmea buffer overflow, discarding '%s'\n", buffer);
101 dbg(1,"data->set=0x%x\n", data->set);
102 if (data->set & SPEED_SET) {
103 priv->speed = data->fix.speed * 3.6;
104 if(!isnan(data->fix.speed))
105 callback_list_call_attr_0(priv->cbl, attr_position_speed);
106 data->set &= ~SPEED_SET;
108 if (data->set & TRACK_SET) {
109 priv->direction = data->fix.track;
110 data->set &= ~TRACK_SET;
112 if (data->set & ALTITUDE_SET) {
113 priv->height = data->fix.altitude;
114 data->set &= ~ALTITUDE_SET;
116 if (data->set & SATELLITE_SET) {
118 if(data->satellites_visible > 0) {
120 if(data->satellites > 0) {
124 for( i=0;i<data->satellites_visible;i++) {
126 for( i=0;i<data->satellites;i++) {
133 if (priv->sats_used != data->satellites_used || priv->sats != data->satellites_visible || priv->sats_signal != sats_signal ) {
135 if (priv->sats_used != data->satellites_used || priv->sats != data->satellites || priv->sats_signal != sats_signal ) {
137 priv->sats_used = data->satellites_used;
139 priv->sats = data->satellites_visible;
141 priv->sats = data->satellites;
143 priv->sats_signal = sats_signal;
144 callback_list_call_attr_0(priv->cbl, attr_position_sats);
146 data->set &= ~SATELLITE_SET;
148 if (data->set & STATUS_SET) {
149 priv->status = data->status;
150 data->set &= ~STATUS_SET;
152 if (data->set & MODE_SET) {
153 priv->fix_type = data->fix.mode - 1;
154 data->set &= ~MODE_SET;
156 if (data->set & TIME_SET) {
157 priv->fix_time = data->fix.time;
158 data->set &= ~TIME_SET;
161 if (data->set & DOP_SET) {
162 dbg(1, "pdop : %g\n", data->dop.pdop);
163 priv->hdop = data->dop.pdop;
164 data->set &= ~DOP_SET;
166 if (data->set & PDOP_SET) {
167 dbg(1, "pdop : %g\n", data->pdop);
168 priv->hdop = data->hdop;
169 data->set &= ~PDOP_SET;
172 if (data->set & LATLON_SET) {
173 priv->geo.lat = data->fix.latitude;
174 priv->geo.lng = data->fix.longitude;
175 dbg(1,"lat=%f lng=%f\n", priv->geo.lat, priv->geo.lng);
176 g_free(priv->nmea_data);
177 priv->nmea_data=priv->nmea_data_buf;
178 priv->nmea_data_buf=NULL;
179 data->set &= ~LATLON_SET;
181 // If data->fix.speed is NAN, then the drawing gets jumpy.
182 if (! isnan(data->fix.speed) && priv->fix_type > 0) {
183 callback_list_call_attr_0(priv->cbl, attr_position_coord_geo);
189 * Attempt to open the gps device.
190 * Return FALSE if retry not required
191 * Return TRUE to try again
194 vehicle_gpsd_try_open(gpointer *data)
196 struct vehicle_priv *priv = (struct vehicle_priv *)data;
197 char *source = g_strdup(priv->source);
198 char *colon = index(source + 7, ':');
204 dbg(0,"Trying to connect to %s:%s\n",source+7,port?port:"default");
205 priv->gps = gps_open(source + 7, port);
209 dbg(0,"gps_open failed for '%s'. Retrying in %d seconds. Have you started gpsd?\n", priv->source, priv->retry_interval);
213 gps_stream(priv->gps, WATCH_ENABLE, NULL);
215 gps_query(priv->gps, priv->gpsd_query);
217 gps_set_raw_hook(priv->gps, vehicle_gpsd_callback);
218 priv->cb = callback_new_1(callback_cast(vehicle_gpsd_io), priv);
219 priv->evwatch = event_add_watch((void *)priv->gps->gps_fd, event_watch_cond_read, priv->cb);
220 if (!priv->gps->gps_fd) {
221 dbg(0,"Warning: gps_fd is 0, most likely you have used a gps.h incompatible to libgps");
223 dbg(0,"Connected to gpsd fd=%d evwatch=%p\n", priv->gps->gps_fd, priv->evwatch);
228 * Open a connection to gpsd. Will re-try the connection if it fails
231 vehicle_gpsd_open(struct vehicle_priv *priv)
234 char errstr[256] = "";
235 /* We need to start gpsd (via gpsbt) first. */
237 memset(&priv->context, 0, sizeof(gpsbt_t));
238 if(gpsbt_start(NULL, 0, 0, 0, errstr, sizeof(errstr),
239 0, &priv->context) < 0) {
240 dbg(0,"Error connecting to GPS with gpsbt: (%d) %s (%s)\n",
241 errno, strerror(errno), errstr);
243 sleep(1); /* give gpsd time to start */
244 dbg(1,"gpsbt_start: completed\n");
247 if (vehicle_gpsd_try_open((gpointer *)priv)) {
248 priv->retry_timer = g_timeout_add(priv->retry_interval*1000, (GSourceFunc)vehicle_gpsd_try_open, (gpointer *)priv);
253 vehicle_gpsd_close(struct vehicle_priv *priv)
259 if (priv->retry_timer) {
260 g_source_remove(priv->retry_timer);
264 event_remove_watch(priv->evwatch);
265 priv->evwatch = NULL;
268 callback_destroy(priv->cb);
272 gps_close(priv->gps);
276 err = gpsbt_stop(&priv->context);
278 dbg(0,"Error %d while gpsbt_stop", err);
280 dbg(1,"gpsbt_stop: completed, (%d)",err);
285 vehicle_gpsd_io(struct vehicle_priv *priv)
290 if (gps_poll(priv->gps)) {
291 g_warning("gps_poll failed\n");
292 vehicle_gpsd_close(priv);
293 vehicle_gpsd_open(priv);
299 vehicle_gpsd_destroy(struct vehicle_priv *priv)
301 vehicle_gpsd_close(priv);
303 g_free(priv->source);
304 #ifndef HAVE_LIBGPS19
305 if (priv->gpsd_query)
306 g_free(priv->gpsd_query);
312 vehicle_gpsd_position_attr_get(struct vehicle_priv *priv,
313 enum attr_type type, struct attr *attr)
315 struct attr * active=NULL;
317 case attr_position_fix_type:
318 attr->u.num = priv->fix_type;
320 case attr_position_height:
321 attr->u.numd = &priv->height;
323 case attr_position_speed:
324 attr->u.numd = &priv->speed;
326 case attr_position_direction:
327 attr->u.numd = &priv->direction;
329 case attr_position_hdop:
330 attr->u.numd = &priv->hdop;
332 case attr_position_qual:
333 attr->u.num = priv->sats;
335 case attr_position_sats_signal:
336 attr->u.num = priv->sats_signal;
338 case attr_position_sats_used:
339 attr->u.num = priv->sats_used;
341 case attr_position_coord_geo:
342 attr->u.coord_geo = &priv->geo;
344 case attr_position_nmea:
345 attr->u.str=priv->nmea_data;
349 case attr_position_time_iso8601:
354 if (gmtime_r(&priv->fix_time, &tm)) {
355 strftime(priv->fixiso8601, sizeof(priv->fixiso8601),
356 "%Y-%m-%dT%TZ", &tm);
357 attr->u.str=priv->fixiso8601;
363 active = attr_search(priv->attrs,NULL,attr_active);
365 attr->u.num=active->u.num;
377 struct vehicle_methods vehicle_gpsd_methods = {
378 vehicle_gpsd_destroy,
379 vehicle_gpsd_position_attr_get,
382 static struct vehicle_priv *
383 vehicle_gpsd_new_gpsd(struct vehicle_methods
384 *meth, struct callback_list
385 *cbl, struct attr **attrs)
387 struct vehicle_priv *ret;
389 struct attr *source, *retry_int;
391 struct attr *source, *query, *retry_int;
395 source = attr_search(attrs, NULL, attr_source);
396 ret = g_new0(struct vehicle_priv, 1);
397 ret->source = g_strdup(source->u.str);
398 #ifndef HAVE_LIBGPS19
399 query = attr_search(attrs, NULL, attr_gpsd_query);
401 ret->gpsd_query = g_strconcat(query->u.str, "\n", NULL);
403 ret->gpsd_query = g_strdup("w+x\n");
405 dbg(1,"Format string for gpsd_query: %s\n",ret->gpsd_query);
407 retry_int = attr_search(attrs, NULL, attr_retry_interval);
409 ret->retry_interval = retry_int->u.num;
410 if (ret->retry_interval < MIN_RETRY_INTERVAL) {
411 dbg(0, "Retry interval %d too small, setting to %d\n", ret->retry_interval, MIN_RETRY_INTERVAL);
412 ret->retry_interval = MIN_RETRY_INTERVAL;
415 dbg(1, "Retry interval not defined, setting to %d\n", DEFAULT_RETRY_INTERVAL);
416 ret->retry_interval = DEFAULT_RETRY_INTERVAL;
419 *meth = vehicle_gpsd_methods;
421 vehicle_gpsd_open(ret);
429 plugin_register_vehicle_type("gpsd", vehicle_gpsd_new_gpsd);