Contents of /trunk/src/gps.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 282 - (hide annotations)
Wed May 26 19:21:47 2010 UTC (14 years ago) by harbaum
File MIME type: text/plain
File size: 19787 byte(s)
New gps integration
1 harbaum 1 /*
2 harbaum 282 * Copyright (C) 2009 Till Harbaum <till@harbaum.org>.
3 harbaum 1 *
4 harbaum 282 * This file is part of Maep.
5 harbaum 1 *
6 harbaum 282 * Maep is free software: you can redistribute it and/or modify
7 harbaum 1 * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11 harbaum 282 * Maep is distributed in the hope that it will be useful,
12 harbaum 1 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17 harbaum 282 * along with Maep. If not, see <http://www.gnu.org/licenses/>.
18 harbaum 1 */
19    
20 harbaum 282 /*
21     * This version focusses on low power consumption. This means that
22     * a component can register for GPS callbacks for certain events
23     * (e.g. position change, altitude change, track change ...) and it
24     * will only get notified if one of these changes. Also it's been
25     * verified that the change in question exceed a certain limit.
26     */
27    
28 harbaum 1 #include <stdio.h>
29     #include <string.h>
30     #include <math.h>
31 harbaum 282 #include <stdlib.h>
32 harbaum 1
33     #ifdef USE_MAEMO
34     #ifdef ENABLE_GPSBT
35     #include <gpsbt.h>
36     #include <gpsmgr.h>
37     #endif
38     #include <errno.h>
39 harbaum 282 #else
40     #ifdef USE_LIBGPS
41     #include <gps.h>
42     #include <locale.h>
43 harbaum 1 #endif
44 harbaum 282 #endif
45 harbaum 1
46 harbaum 282 #include "gps.h"
47 harbaum 156
48 harbaum 282 static void gps_unregister_all(gps_state_t *gps_state);
49 harbaum 1
50 harbaum 282 /* limits below which a fix is considered different */
51     #define TRACK_PRECISION (1.0) // one degree
52     #define POS_PRECISION (1.0/60000.0) // 0.001 minute
53     #define EPH_PRECISION (2.0) // 2 meters
54     #define ALTITUDE_PRECISION (1.0) // 1 meters
55 harbaum 1
56 harbaum 282 static gboolean
57     track_differs(struct gps_t *fix1, struct gps_t *fix2) {
58     return( fabsf(fix1->track - fix2->track) >= TRACK_PRECISION);
59     }
60 harbaum 1
61 harbaum 282 static gboolean
62     latlon_differs(struct gps_t *fix1, struct gps_t *fix2) {
63     if( fabsf(fix1->latitude - fix2->latitude) >= POS_PRECISION)
64     return TRUE;
65 harbaum 1
66 harbaum 282 if( fabsf(fix1->longitude - fix2->longitude) >= POS_PRECISION)
67     return TRUE;
68 harbaum 1
69 harbaum 282 return FALSE;
70     }
71 harbaum 1
72 harbaum 282 static gboolean
73     eph_differs(struct gps_t *fix1, struct gps_t *fix2) {
74     return( fabsf(fix1->eph - fix2->eph) >= EPH_PRECISION);
75     }
76 harbaum 1
77 harbaum 282 static gboolean
78     altitude_differs(struct gps_t *fix1, struct gps_t *fix2) {
79     return( fabsf(fix1->altitude - fix2->altitude) >= ALTITUDE_PRECISION);
80 harbaum 1 }
81    
82 harbaum 282 static void gps_cb_func(gpointer data, gpointer user_data) {
83     gps_callback_t *callback = (gps_callback_t*)data;
84     gps_state_t *gps_state = (gps_state_t*)user_data;
85 harbaum 1
86 harbaum 282 /* we can save some energy here if we make sure that */
87     /* we really only wake up the main app whenever the */
88     /* gps state changed in a way that the map needs to */
89     /* be updated */
90    
91     /* for all data check if its status has changed (data is available */
92     /* or not) or if the data itself changed */
93 harbaum 1
94 harbaum 282 /* clear "changed" bits */
95     gps_state->set &= ~CHANGED_MASK;
96 harbaum 1
97 harbaum 282 /* check for changes in position */
98     if(callback->mask & LATLON_CHANGED &&
99     (((gps_state->set & FIX_LATLON_SET) !=
100     (gps_state->last.set & FIX_LATLON_SET)) ||
101     ((gps_state->set & FIX_LATLON_SET) &&
102     latlon_differs(&gps_state->last.fix, &gps_state->fix))))
103     gps_state->set |= LATLON_CHANGED;
104 harbaum 1
105 harbaum 282 /* check for changes in eph */
106     if(callback->mask & HERR_CHANGED &&
107     (((gps_state->set & FIX_HERR_SET) !=
108     (gps_state->last.set & FIX_HERR_SET)) ||
109     ((gps_state->set & FIX_HERR_SET) &&
110     eph_differs(&gps_state->last.fix, &gps_state->fix))))
111     gps_state->set |= HERR_CHANGED;
112 harbaum 1
113 harbaum 282 /* check for changes in track */
114     if(callback->mask & TRACK_CHANGED &&
115     (((gps_state->set & FIX_TRACK_SET) !=
116     (gps_state->last.set & FIX_TRACK_SET)) ||
117     ((gps_state->set & FIX_TRACK_SET) &&
118     track_differs(&gps_state->last.fix, &gps_state->fix))))
119     gps_state->set |= TRACK_CHANGED;
120    
121     /* check for changes in altitude */
122     if(callback->mask & ALTITUDE_CHANGED &&
123     (((gps_state->set & FIX_ALTITUDE_SET) !=
124     (gps_state->last.set & FIX_ALTITUDE_SET)) ||
125     ((gps_state->set & FIX_ALTITUDE_SET) &&
126     altitude_differs(&gps_state->last.fix, &gps_state->fix))))
127     gps_state->set |= ALTITUDE_CHANGED;
128    
129     if(gps_state->set & CHANGED_MASK)
130     callback->cb(gps_state->set, &gps_state->fix, callback->data);
131 harbaum 1 }
132    
133 harbaum 282 #ifndef ENABLE_LIBLOCATION
134 harbaum 1
135 harbaum 282 static gboolean gps_notify(gpointer data) {
136     gps_state_t *gps_state = (gps_state_t*)data;
137 harbaum 1
138 harbaum 282 if(gps_state->callbacks) {
139     g_mutex_lock(gps_state->mutex);
140 harbaum 1
141 harbaum 282 /* tell all clients */
142     g_slist_foreach(gps_state->callbacks, gps_cb_func, gps_state);
143 harbaum 1
144 harbaum 282 g_mutex_unlock(gps_state->mutex);
145     }
146 harbaum 1
147 harbaum 282 /* remember last state reported */
148     gps_state->last.set = gps_state->set;
149     gps_state->last.fix = gps_state->fix;
150    
151     return FALSE;
152 harbaum 1 }
153    
154 harbaum 282 #ifndef USE_LIBGPS
155    
156     /* maybe user configurable later on ... */
157     #define GPSD_HOST "127.0.0.1"
158     #define GPSD_PORT 2947
159    
160 harbaum 1 static int gps_connect(gps_state_t *gps_state) {
161     GnomeVFSResult vfs_result;
162 harbaum 282
163 harbaum 1 #ifdef USE_MAEMO
164     char errstr[256] = "";
165    
166 harbaum 160 if(!gps_state) {
167     printf("No gps state\n");
168     return -1;
169     }
170    
171 harbaum 1 /* We need to start gpsd (via gpsbt) first. */
172     memset(&gps_state->context, 0, sizeof(gpsbt_t));
173     errno = 0;
174    
175     if(gpsbt_start(NULL, 0, 0, 0, errstr, sizeof(errstr),
176     0, &gps_state->context) < 0) {
177 harbaum 156 printf("gps: Error connecting to GPS receiver: (%d) %s (%s)\n",
178 harbaum 1 errno, strerror(errno), errstr);
179     }
180     #endif
181    
182     /************** from here down pure gnome/gtk/gpsd ********************/
183    
184     /* try to connect to gpsd */
185     /* Create a socket to interact with GPSD. */
186    
187     int retries = 5;
188     while(retries &&
189     (GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create(
190     &gps_state->iconn, GPSD_HOST, GPSD_PORT, NULL)))) {
191 harbaum 156 printf("gps: Error creating connection to GPSD, retrying ...\n");
192 harbaum 1
193     retries--;
194     sleep(1);
195     }
196    
197     if(!retries) {
198 harbaum 156 printf("gps: Finally failed ...\n");
199 harbaum 1 return -1;
200     }
201    
202     retries = 5;
203     while(retries && ((gps_state->socket =
204     gnome_vfs_inet_connection_to_socket(gps_state->iconn)) == NULL)) {
205 harbaum 282 printf("gps: Error connecting GPSD socket, retrying ...\n");
206 harbaum 1
207     retries--;
208     sleep(1);
209     }
210    
211     if(!retries) {
212 harbaum 156 printf("gps: Finally failed ...\n");
213 harbaum 1 return -1;
214     }
215    
216     GTimeVal timeout = { 10, 0 };
217     if(GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout(
218     gps_state->socket, &timeout, NULL))) {
219 harbaum 156 printf("gps: Error setting GPSD timeout\n");
220 harbaum 1 return -1;
221     }
222    
223 harbaum 156 printf("gps: GPSD connected ...\n");
224 harbaum 1
225     return 0;
226     }
227    
228 harbaum 282 static double parse_double(char *val) {
229     if(val[0] == '?') return NAN;
230     return g_ascii_strtod(val, NULL);
231 harbaum 1 }
232    
233     /* unpack a daemon response into a status structure */
234 harbaum 282 static void gps_unpack(char *buf, gps_state_t *gps_state) {
235 harbaum 1 char *ns, *sp, *tp;
236 harbaum 282 int j;
237 harbaum 1
238     for(ns = buf; ns; ns = strstr(ns+1, "GPSD")) {
239     if(strncmp(ns, "GPSD", 4) == 0) {
240     /* the following should execute each time we have a good next sp */
241     for (sp = ns + 5; *sp != '\0'; sp = tp+1) {
242     tp = sp + strcspn(sp, ",\r\n");
243     if (*tp == '\0') tp--;
244     else *tp = '\0';
245    
246     switch (*sp) {
247     case 'O':
248     if (sp[2] == '?') {
249 harbaum 282 gps_state->fix.latitude = NAN;
250     gps_state->fix.longitude = NAN;
251     gps_state->fix.altitude = NAN;
252     gps_state->fix.track = NAN;
253     gps_state->fix.eph = NAN;
254 harbaum 1 } else {
255     char tag[MAXTAGLEN+1], alt[20];
256 harbaum 282 char eph[20], track[20],speed[20];
257     char lat[20], lon[20];
258     if(sscanf(sp+2,
259     "%8s %*s %*s %19s %19s "
260     "%19s %19s %*s %19s %19s %*s "
261     "%*s %*s %*s %*s",
262     tag, lat, lon,
263     alt, eph, track, speed) == 7) {
264    
265     gps_state->fix.latitude = parse_double(lat);
266     gps_state->fix.longitude = parse_double(lon);
267     gps_state->fix.altitude = parse_double(alt);
268     gps_state->fix.eph = parse_double(eph);
269     gps_state->fix.track = parse_double(track);
270    
271     gps_state->set |= FIX_LATLON_SET;
272    
273     if (!isnan(gps_state->fix.eph))
274     gps_state->set |= FIX_HERR_SET;
275    
276     if (!isnan(gps_state->fix.track))
277     gps_state->set |= FIX_TRACK_SET;
278    
279     if(!isnan(gps_state->fix.altitude))
280     gps_state->set |= FIX_ALTITUDE_SET;
281 harbaum 1 }
282     }
283     break;
284 harbaum 282
285     case 'Y':
286     gps_state->fix.sat_num = 0;
287    
288     if (sp[2] != '?') {
289     (void)sscanf(sp, "Y=%*s %*s %d ", &gps_state->fix.sat_num);
290    
291     /* clear all slots */
292     for (j = 0; j < gps_state->fix.sat_num; j++)
293     gps_state->fix.sat_data[j].prn =
294     gps_state->fix.sat_data[j].ss =
295     gps_state->fix.sat_data[j].used = 0;
296    
297     for (j = 0; j < gps_state->fix.sat_num; j++) {
298     if ((sp != NULL) && ((sp = strchr(sp, ':')) != NULL)) {
299     sp++;
300     (void)sscanf(sp, "%d %*d %*d %d %d",
301     &gps_state->fix.sat_data[j].prn,
302     &gps_state->fix.sat_data[j].ss,
303     &gps_state->fix.sat_data[j].used);
304     }
305     }
306     }
307     gps_state->set |= FIX_SATELLITE_SET;
308 harbaum 1 break;
309     }
310     }
311     }
312     }
313     }
314    
315 harbaum 282 static gpointer gps_thread(gpointer data) {
316     gps_state_t *gps_state = (gps_state_t*)data;
317    
318 harbaum 1 GnomeVFSFileSize bytes_read;
319     GnomeVFSResult vfs_result;
320     char str[512];
321    
322 harbaum 282 gps_state->set = 0;
323 harbaum 1
324     gboolean connected = FALSE;
325    
326 harbaum 282 int cnt=1000;
327    
328     const char *msg_pos = "o\r\n"; /* pos request */
329     const char *msg_sat = "y\r\n"; /* sat request */
330    
331 harbaum 1 while(1) {
332 harbaum 282 /* just lock and unlock the control mutex. This stops the thread */
333     /* while the main process locks this mutex */
334     g_mutex_lock(gps_state->control_mutex);
335     g_mutex_unlock(gps_state->control_mutex);
336     g_mutex_lock(gps_state->global_mutex);
337     g_mutex_unlock(gps_state->global_mutex);
338 harbaum 1
339 harbaum 282 if(!connected) {
340     printf("gps: trying to connect\n");
341    
342     if(gps_connect(gps_state) < 0)
343     sleep(10);
344     else
345     connected = TRUE;
346    
347     } else {
348     const char *msg;
349     if(!cnt) msg = msg_sat;
350     else msg = msg_pos;
351    
352     if(GNOME_VFS_OK ==
353     (vfs_result = gnome_vfs_socket_write(gps_state->socket,
354 harbaum 1 msg, strlen(msg)+1, &bytes_read, NULL))) {
355    
356 harbaum 282 /* update every second, wait here to make sure a complete */
357     /* reply is received */
358     if(cnt <= 1) usleep(500000);
359     else sleep(1);
360    
361     if(bytes_read == (strlen(msg)+1)) {
362     vfs_result = gnome_vfs_socket_read(gps_state->socket,
363     str, sizeof(str)-1, &bytes_read, NULL);
364    
365     if(vfs_result == GNOME_VFS_OK) {
366     str[bytes_read] = 0;
367    
368     g_mutex_lock(gps_state->mutex);
369    
370     /* assume we could't parse anything ... */
371     if(!cnt) gps_state->set &= ~FIX_SATELLITE_SET;
372     else gps_state->set &=
373     ~(FIX_LATLON_SET|FIX_HERR_SET|FIX_ALTITUDE_SET|FIX_TRACK_SET);
374 harbaum 1
375 harbaum 282 gps_unpack(str, gps_state);
376    
377     /* notify applications of state if useful */
378     g_idle_add(gps_notify, gps_state);
379    
380     g_mutex_unlock(gps_state->mutex);
381 harbaum 1 }
382     }
383 harbaum 282
384     /* if we have clients requesting sat information return to */
385     /* state 0 which will request them. Otherwise loop in states */
386     /* 1 - 4 which don't request them */
387     if(cnt++ >= 5) {
388     if(gps_state->sat_requests) cnt = 0;
389     else cnt = 1;
390     }
391 harbaum 1 }
392 harbaum 282 }
393     }
394 harbaum 1
395 harbaum 282 printf("gps: thread ended???\n");
396     return NULL;
397     }
398    
399     gps_state_t *gps_init(void) {
400     gps_state_t *gps_state = g_new0(gps_state_t, 1);
401    
402     /* start a new thread to listen to gpsd */
403     gps_state->mutex = g_mutex_new();
404     gps_state->control_mutex = g_mutex_new();
405     gps_state->global_mutex = g_mutex_new();
406     gps_state->thread_p =
407     g_thread_create(gps_thread, gps_state, FALSE, NULL);
408    
409     return gps_state;
410     }
411    
412     void gps_release(gps_state_t *gps_state) {
413     gps_unregister_all(gps_state);
414    
415 harbaum 1 #ifdef USE_MAEMO
416 harbaum 282 gpsbt_stop(&gps_state->context);
417 harbaum 1 #endif
418 harbaum 282
419     g_free(gps_state);
420     }
421    
422     #else // USE_LIBGPS
423    
424     static gpointer gps_thread(gpointer data) {
425     gps_state_t *gps_state = (gps_state_t*)data;
426    
427     /* the following is required for libgps to be able to parse */
428     /* the gps messages. Unfortunately this also affect the rest of */
429     /* the program and the main thread */
430     setlocale(LC_NUMERIC, "C");
431    
432     while(1) {
433     /* just lock and unlock the control mutex. This stops the thread */
434     /* while the main process locks this mutex */
435     g_mutex_lock(gps_state->control_mutex);
436     g_mutex_unlock(gps_state->control_mutex);
437    
438     gps_poll(gps_state->data);
439    
440     g_mutex_lock(gps_state->mutex);
441    
442     /* assume we could't parse anything ... */
443     gps_state->set = 0;
444    
445     if(gps_state->data->fix.mode >= MODE_2D) {
446     /* latlon valid */
447     gps_state->set |= FIX_LATLON_SET;
448     gps_state->fix.latitude = gps_state->data->fix.latitude;
449     gps_state->fix.longitude = gps_state->data->fix.longitude;
450    
451     gps_state->fix.eph =
452     gps_state->data->fix.epy > gps_state->data->fix.epx ?
453     gps_state->data->fix.epy : gps_state->data->fix.epx;
454     if(!isnan(gps_state->fix.eph)) gps_state->set |= FIX_HERR_SET;
455    
456     gps_state->fix.track = gps_state->data->fix.track;
457     if(!isnan(gps_state->fix.track)) gps_state->set |= FIX_TRACK_SET;
458 harbaum 1 }
459 harbaum 282
460     if(gps_state->data->fix.mode >= MODE_3D) {
461     /* altitude valid */
462     gps_state->set |= FIX_ALTITUDE_SET;
463     gps_state->fix.altitude = gps_state->data->fix.altitude;
464     } else
465     gps_state->fix.altitude = NAN;
466    
467     /* notify applications of state if useful */
468     g_idle_add(gps_notify, gps_state);
469    
470     g_mutex_unlock(gps_state->mutex);
471    
472 harbaum 1 }
473    
474 harbaum 156 printf("gps: thread ended???\n");
475 harbaum 1 return NULL;
476     }
477    
478 harbaum 282 gps_state_t *gps_init(void) {
479     gps_state_t *gps_state = g_new0(gps_state_t, 1);
480 harbaum 1
481 harbaum 282 gps_state->mutex = g_mutex_new();
482     gps_state->control_mutex = g_mutex_new();
483    
484     gps_state->data = gps_open("127.0.0.1", DEFAULT_GPSD_PORT );
485     if(!gps_state->data)
486     perror("gps_open()");
487    
488     (void)gps_stream(gps_state->data, WATCH_ENABLE, NULL);
489    
490     gps_state->thread_p =
491     g_thread_create(gps_thread, gps_state, FALSE, NULL);
492    
493     return gps_state;
494 harbaum 1 }
495    
496 harbaum 282 void gps_release(gps_state_t *gps_state) {
497     gps_unregister_all(gps_state);
498     gps_close(gps_state->data);
499     g_free(gps_state);
500 harbaum 243 }
501    
502 harbaum 282 #endif // USE_LIBGPS
503    
504     static void gps_background_enable(gps_state_t *gps_state, gboolean enable) {
505     printf("GPS: %sable background process\n", enable?"en":"dis");
506    
507     /* start and stop gps thread by locking and unlocking the control mutex */
508     if(enable) g_mutex_unlock(gps_state->control_mutex);
509     else g_mutex_lock(gps_state->control_mutex);
510 harbaum 1 }
511    
512 harbaum 282 void gps_enable(gps_state_t *gps_state, gboolean enable) {
513     /* just ignore request if we are already in that state */
514     if(gps_state->stopped == !enable) return;
515    
516     if(!enable) g_mutex_lock(gps_state->global_mutex);
517     else g_mutex_unlock(gps_state->global_mutex);
518    
519     gps_state->stopped = !enable;
520     }
521    
522    
523 harbaum 1 #else
524    
525     static void
526     location_changed(LocationGPSDevice *device, gps_state_t *gps_state) {
527    
528 harbaum 282 gps_state->set = 0;
529 harbaum 8
530 harbaum 282 if(device->fix->fields & LOCATION_GPS_DEVICE_LATLONG_SET) {
531     gps_state->set |= FIX_LATLON_SET | FIX_HERR_SET;
532     gps_state->fix.latitude = device->fix->latitude;
533     gps_state->fix.longitude = device->fix->longitude;
534 harbaum 233 gps_state->fix.eph = device->fix->eph/100.0; // we want eph in meters
535 harbaum 8 }
536 harbaum 7
537 harbaum 282 if(device->fix->fields & LOCATION_GPS_DEVICE_ALTITUDE_SET) {
538     gps_state->set |= FIX_ALTITUDE_SET;
539     gps_state->fix.altitude = device->fix->altitude;
540     } else
541     gps_state->fix.altitude = NAN;
542    
543     if(device->fix->fields & LOCATION_GPS_DEVICE_TRACK_SET) {
544     gps_state->set |= FIX_TRACK_SET;
545 harbaum 233 gps_state->fix.track = device->fix->track;
546 harbaum 1 }
547 harbaum 8
548 harbaum 282 /* tell all clients */
549     g_slist_foreach(gps_state->callbacks, gps_cb_func, gps_state);
550 harbaum 8
551 harbaum 282 /* remember last state reported */
552     gps_state->last.set = gps_state->set;
553     gps_state->last.fix = gps_state->fix;
554 harbaum 1 }
555    
556 harbaum 282 gps_state_t *gps_init(void) {
557     gps_state_t *gps_state = g_new0(gps_state_t, 1);
558 harbaum 1
559     gps_state->device = g_object_new(LOCATION_TYPE_GPS_DEVICE, NULL);
560     if(!gps_state->device) {
561 harbaum 156 printf("gps: Unable to connect to liblocation\n");
562 harbaum 282 g_free(gps_state);
563     return NULL;
564 harbaum 1 }
565    
566     gps_state->idd_changed =
567     g_signal_connect(gps_state->device, "changed",
568     G_CALLBACK(location_changed), gps_state);
569 harbaum 282 gps_state->connected = TRUE;
570 harbaum 1
571 harbaum 11 gps_state->control = location_gpsd_control_get_default();
572    
573 harbaum 36 if(gps_state->control
574     #if MAEMO_VERSION_MAJOR < 5
575     && gps_state->control->can_control
576     #endif
577     ) {
578    
579 harbaum 156 printf("gps: Having control over GPSD and GPS is to be enabled, starting it\n");
580 harbaum 11 location_gpsd_control_start(gps_state->control);
581     }
582 harbaum 282 return gps_state;
583 harbaum 1 }
584    
585 harbaum 282 void gps_release(gps_state_t *gps_state) {
586     gps_unregister_all(gps_state);
587 harbaum 11
588 harbaum 36 if(gps_state->control
589 harbaum 11 #if MAEMO_VERSION_MAJOR < 5
590 harbaum 36 && gps_state->control->can_control
591     #endif
592     ) {
593 harbaum 156 printf("gps: Having control over GPSD, stopping it\n");
594 harbaum 11 location_gpsd_control_stop(gps_state->control);
595     }
596 harbaum 1
597     /* Disconnect signal */
598     g_signal_handler_disconnect(gps_state->device, gps_state->idd_changed);
599 harbaum 282 gps_state->connected = FALSE;
600 harbaum 1
601 harbaum 282 g_free(gps_state);
602 harbaum 1 }
603    
604 harbaum 282 static void gps_control(gps_state_t *gps_state,
605     gboolean enable0, gboolean enable1) {
606     if(!gps_state->connected) {
607     /* both "enable" signals have to be true */
608     if(enable0 && enable1) {
609     printf("GPS: connecting\n");
610     gps_state->idd_changed =
611     g_signal_connect(gps_state->device, "changed",
612     G_CALLBACK(location_changed), gps_state);
613     gps_state->connected = TRUE;
614     }
615     } else {
616     /* at least one "enable" is false */
617     if(!enable0 || !enable1) {
618     printf("GPS: disconnecting\n");
619     /* Disconnect signal */
620     g_signal_handler_disconnect(gps_state->device, gps_state->idd_changed);
621     gps_state->connected = FALSE;
622     }
623     }
624     }
625 harbaum 1
626 harbaum 282 /* enable/disable due to all windows being in background */
627     void gps_background_enable(gps_state_t *gps_state, gboolean enable) {
628     if(gps_state->backgrounded == !enable) return;
629 harbaum 1
630 harbaum 282 gps_state->backgrounded = !enable;
631     gps_control(gps_state, gps_state->stopped, gps_state->backgrounded);
632 harbaum 1 }
633    
634 harbaum 282 /* enable/disable on app request */
635     void gps_enable(gps_state_t *gps_state, gboolean enable) {
636     if(gps_state->stopped == !enable) return;
637 harbaum 1
638 harbaum 282 gps_state->stopped = !enable;
639     gps_control(gps_state, gps_state->stopped, gps_state->backgrounded);
640 harbaum 7 }
641 harbaum 1
642 harbaum 282 #endif // USE_LIBLOCATION
643 harbaum 7
644 harbaum 282 static gint compare(gconstpointer a, gconstpointer b) {
645     return ((gps_callback_t*)a)->cb != b;
646 harbaum 7 }
647    
648 harbaum 282 void gps_register_callback(gps_state_t *gps_state, int mask,
649     gps_cb cb, void *data) {
650     /* make sure the callback isn't already registered */
651     GSList *list = g_slist_find_custom(gps_state->callbacks, cb, compare);
652     if(list) {
653     printf("GPS register: ignoring duplicate\n");
654     return;
655 harbaum 8 }
656 harbaum 1
657 harbaum 282 gps_callback_t *callback = g_new0(gps_callback_t, 1);
658 harbaum 8
659 harbaum 282 g_assert(mask & CHANGED_MASK);
660     g_assert(!(mask & ~CHANGED_MASK));
661    
662     callback->mask = mask;
663     callback->cb = cb;
664     callback->data = data;
665 harbaum 8
666 harbaum 282 #if !defined(ENABLE_LIBLOCATION) && !defined(USE_LIBGPS)
667     /* the gpsd solution needs to know the number of clients */
668     /* requesting sat information */
669     if(mask & SATELLITE_CHANGED)
670     gps_state->sat_requests++;
671 harbaum 1 #endif
672 harbaum 149
673 harbaum 282 gps_state->callbacks = g_slist_append(gps_state->callbacks, callback);
674    
675     if(g_slist_length(gps_state->callbacks) == 1)
676     gps_background_enable(gps_state, TRUE);
677 harbaum 149 }
678    
679 harbaum 282 void gps_unregister_callback(gps_state_t *gps_state, gps_cb cb) {
680     /* find callback in list */
681     GSList *list = g_slist_find_custom(gps_state->callbacks, cb, compare);
682     g_assert(list);
683 harbaum 149
684 harbaum 282 #if !defined(ENABLE_LIBLOCATION) && !defined(USE_LIBGPS)
685     /* the gpsd solution needs to know the number of clients */
686     /* requesting sat information */
687     gps_callback_t *cb_s = list->data;
688     if(cb_s->mask & SATELLITE_CHANGED)
689     gps_state->sat_requests--;
690     #endif
691 harbaum 156
692 harbaum 282 /* and de-chain and free it */
693     g_free(list->data);
694     gps_state->callbacks = g_slist_remove(gps_state->callbacks, list->data);
695 harbaum 156
696 harbaum 282 if(g_slist_length(gps_state->callbacks) == 0)
697     gps_background_enable(gps_state, FALSE);
698 harbaum 149 }
699 harbaum 156
700 harbaum 282 static void gps_unregister_all(gps_state_t *gps_state) {
701     g_slist_foreach(gps_state->callbacks, (GFunc)g_free, NULL);
702     g_slist_free(gps_state->callbacks);
703 harbaum 156 }