]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gps.c
Fix some memory leaks. Fixes: #2099.
[maemo-mapper] / src / gps.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #    include "config.h"
26 #endif
27
28 #define _GNU_SOURCE
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33
34 #include <libgnomevfs/gnome-vfs.h>
35 #include <libgnomevfs/gnome-vfs-inet-connection.h>
36 #include <errno.h>
37
38 #ifndef LEGACY
39 #    include <hildon/hildon-note.h>
40 #    include <hildon/hildon-banner.h>
41 #else
42 #    include <hildon-widgets/hildon-note.h>
43 #    include <hildon-widgets/hildon-banner.h>
44 #endif
45
46 #include "types.h"
47 #include "data.h"
48 #include "defines.h"
49
50 #include "display.h"
51 #include "gps.h"
52 #include "gpsbt.h"
53 #include "path.h"
54 #include "util.h"
55
56 static volatile GThread *_gps_thread = NULL;
57 static GMutex *_gps_init_mutex = NULL;
58 static gint _gps_rcvr_retry_count = 0;
59
60 static gint _gmtoffset = 0;
61
62
63 #define MACRO_PARSE_INT(tofill, str) { \
64     gchar *error_check; \
65     (tofill) = strtol((str), &error_check, 10); \
66     if(error_check == (str)) \
67     { \
68         g_printerr("Line %d: Failed to parse string as int: %s\n", \
69                 __LINE__, str); \
70         MACRO_BANNER_SHOW_INFO(_window, \
71                 _("Invalid NMEA input from receiver!")); \
72         return; \
73     } \
74 }
75 #define MACRO_PARSE_FLOAT(tofill, str) { \
76     gchar *error_check; \
77     (tofill) = g_ascii_strtod((str), &error_check); \
78     if(error_check == (str)) \
79     { \
80         g_printerr("Failed to parse string as float: %s\n", str); \
81         MACRO_BANNER_SHOW_INFO(_window, \
82                 _("Invalid NMEA input from receiver!")); \
83         return; \
84     } \
85 }
86 static void
87 gps_parse_rmc(gchar *sentence)
88 {
89     /* Recommended Minimum Navigation Information C
90      *  1) UTC Time
91      *  2) Status, V=Navigation receiver warning A=Valid
92      *  3) Latitude
93      *  4) N or S
94      *  5) Longitude
95      *  6) E or W
96      *  7) Speed over ground, knots
97      *  8) Track made good, degrees true
98      *  9) Date, ddmmyy
99      * 10) Magnetic Variation, degrees
100      * 11) E or W
101      * 12) FAA mode indicator (NMEA 2.3 and later)
102      * 13) Checksum
103      */
104     gchar *token, *dpoint, *gpsdate = NULL;
105     gdouble tmpd = 0.f;
106     gint tmpi = 0;
107     gboolean newly_fixed = FALSE;
108     vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
109
110 #define DELIM ","
111
112     /* Parse time. */
113     token = strsep(&sentence, DELIM);
114     if(token && *token)
115         gpsdate = token;
116
117     token = strsep(&sentence, DELIM);
118     /* Token is now Status. */
119     if(token && *token == 'A')
120     {
121         /* Data is valid. */
122         if(_gps_state < RCVR_FIXED)
123         {
124             newly_fixed = TRUE;
125             set_conn_state(RCVR_FIXED);
126         }
127     }
128     else
129     {
130         /* Data is invalid - not enough satellites?. */
131         if(_gps_state > RCVR_UP)
132         {
133             set_conn_state(RCVR_UP);
134             track_insert_break(FALSE);
135         }
136     }
137
138     /* Parse the latitude. */
139     token = strsep(&sentence, DELIM);
140     if(token && *token)
141     {
142         dpoint = strchr(token, '.');
143         if(!dpoint) /* handle buggy NMEA */
144             dpoint = token + strlen(token);
145         MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
146         dpoint[-2] = '\0';
147         MACRO_PARSE_INT(tmpi, token);
148         _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
149     }
150
151     /* Parse N or S. */
152     token = strsep(&sentence, DELIM);
153     if(token && *token == 'S')
154         _gps.lat = -_gps.lat;
155
156     /* Parse the longitude. */
157     token = strsep(&sentence, DELIM);
158     if(token && *token)
159     {
160         dpoint = strchr(token, '.');
161         if(!dpoint) /* handle buggy NMEA */
162             dpoint = token + strlen(token);
163         MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
164         dpoint[-2] = '\0';
165         MACRO_PARSE_INT(tmpi, token);
166         _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
167     }
168
169     /* Parse E or W. */
170     token = strsep(&sentence, DELIM);
171     if(token && *token == 'W')
172         _gps.lon = -_gps.lon;
173
174     /* Parse speed over ground, knots. */
175     token = strsep(&sentence, DELIM);
176     if(token && *token)
177     {
178         MACRO_PARSE_FLOAT(_gps.speed, token);
179         if(_gps.fix > 1)
180             _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
181     }
182
183     /* Parse heading, degrees from true north. */
184     token = strsep(&sentence, DELIM);
185     if(token && *token)
186     {
187         MACRO_PARSE_FLOAT(_gps.heading, token);
188     }
189
190     /* Parse date. */
191     token = strsep(&sentence, DELIM);
192     if(token && *token && gpsdate)
193     {
194         struct tm time;
195         gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
196         strcat(gpsdate, token);
197         strptime(gpsdate, "%H%M%S%d%m%y", &time);
198         _pos.time = mktime(&time) + _gmtoffset;
199     }
200     else
201         _pos.time = time(NULL);
202
203     /* Translate data into integers. */
204     latlon2unit(_gps.lat, _gps.lon, _pos.unitx, _pos.unity);
205
206     /* Add new data to track. */
207     if(_gps_state == RCVR_FIXED)
208     {
209         if(track_add(_pos.time, newly_fixed) || newly_fixed)
210         {
211             /* Move mark to new location. */
212             map_refresh_mark(FALSE);
213         }
214         else
215         {
216             map_move_mark();
217         }
218     }
219
220     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
221 }
222
223 static void
224 gps_parse_gga(gchar *sentence)
225 {
226     /* GGA          Global Positioning System Fix Data
227      1. Fix Time
228      2. Latitude
229      3. N or S
230      4. Longitude
231      5. E or W
232      6. Fix quality
233                    0 = invalid
234                    1 = GPS fix (SPS)
235                    2 = DGPS fix
236                    3 = PPS fix
237                    4 = Real Time Kinematic
238                    5 = Float RTK
239                    6 = estimated (dead reckoning) (2.3 feature)
240                    7 = Manual input mode
241                    8 = Simulation mode
242      7. Number of satellites being tracked
243      8. Horizontal dilution of precision
244      9. Altitude, Meters, above mean sea level
245      10. Alt unit (meters)
246      11. Height of geoid (mean sea level) above WGS84 ellipsoid
247      12. unit
248      13. (empty field) time in seconds since last DGPS update
249      14. (empty field) DGPS station ID number
250      15. the checksum data
251      */
252     gchar *token;
253     vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
254
255 #define DELIM ","
256
257     /* Skip Fix time */
258     token = strsep(&sentence, DELIM);
259     /* Skip latitude */
260     token = strsep(&sentence, DELIM);
261     /* Skip N or S */
262     token = strsep(&sentence, DELIM);
263     /* Skip longitude */
264     token = strsep(&sentence, DELIM);
265     /* Skip S or W */
266     token = strsep(&sentence, DELIM);
267
268     /* Parse Fix quality */
269     token = strsep(&sentence, DELIM);
270     if(token && *token)
271         MACRO_PARSE_INT(_gps.fixquality, token);
272
273     /* Skip number of satellites */
274     token = strsep(&sentence, DELIM);
275
276     /* Parse Horizontal dilution of precision */
277     token = strsep(&sentence, DELIM);
278     if(token && *token)
279         MACRO_PARSE_INT(_gps.hdop, token);
280
281     /* Altitude */
282     token = strsep(&sentence, DELIM);
283     if(token && *token)
284     {
285         MACRO_PARSE_FLOAT(_pos.altitude, token);
286     }
287     else
288         _pos.altitude = 0;
289
290     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
291 }
292
293 static void
294 gps_parse_gsa(gchar *sentence)
295 {
296     /* GPS DOP and active satellites
297      *  1) Auto selection of 2D or 3D fix (M = manual)
298      *  2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
299      *  3) PRNs of satellites used for fix
300      *     (space for 12)
301      *  4) PDOP (dilution of precision)
302      *  5) Horizontal dilution of precision (HDOP)
303      *  6) Vertical dilution of precision (VDOP)
304      *  7) Checksum
305      */
306     gchar *token;
307     gint i;
308     vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
309
310 #define DELIM ","
311
312     /* Skip Auto selection. */
313     token = strsep(&sentence, DELIM);
314
315     /* 3D fix. */
316     token = strsep(&sentence, DELIM);
317     if(token && *token)
318         MACRO_PARSE_INT(_gps.fix, token);
319
320     _gps.satinuse = 0;
321     for(i = 0; i < 12; i++)
322     {
323         token = strsep(&sentence, DELIM);
324         if(token && *token)
325             _gps.satforfix[_gps.satinuse++] = atoi(token);
326     }
327
328     /* PDOP */
329     token = strsep(&sentence, DELIM);
330     if(token && *token)
331         MACRO_PARSE_FLOAT(_gps.pdop, token);
332
333     /* HDOP */
334     token = strsep(&sentence, DELIM);
335     if(token && *token)
336         MACRO_PARSE_FLOAT(_gps.hdop, token);
337
338     /* VDOP */
339     token = strsep(&sentence, DELIM);
340     if(token && *token)
341         MACRO_PARSE_FLOAT(_gps.vdop, token);
342
343     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
344 }
345
346 static void
347 gps_parse_gsv(gchar *sentence)
348 {
349     /* Must be GSV - Satellites in view
350      *  1) total number of messages
351      *  2) message number
352      *  3) satellites in view
353      *  4) satellite number
354      *  5) elevation in degrees (0-90)
355      *  6) azimuth in degrees to true north (0-359)
356      *  7) SNR in dB (0-99)
357      *  more satellite infos like 4)-7)
358      *  n) checksum
359      */
360     gchar *token;
361     gint msgcnt = 0, nummsgs = 0;
362     static gint running_total = 0;
363     static gint num_sats_used = 0;
364     static gint satcnt = 0;
365     vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
366
367     /* Parse number of messages. */
368     token = strsep(&sentence, DELIM);
369     if(token && *token)
370         MACRO_PARSE_INT(nummsgs, token);
371
372     /* Parse message number. */
373     token = strsep(&sentence, DELIM);
374     if(token && *token)
375         MACRO_PARSE_INT(msgcnt, token);
376
377     /* Parse number of satellites in view. */
378     token = strsep(&sentence, DELIM);
379     if(token && *token)
380     {
381         MACRO_PARSE_INT(_gps.satinview, token);
382         if(_gps.satinview > 12) /* Handle buggy NMEA. */
383             _gps.satinview = 12;
384     }
385
386     /* Loop until there are no more satellites to parse. */
387     while(sentence && satcnt < 12)
388     {
389         /* Get token for Satellite Number. */
390         token = strsep(&sentence, DELIM);
391         if(token && *token)
392             _gps_sat[satcnt].prn = atoi(token);
393
394         /* Get token for elevation in degrees (0-90). */
395         token = strsep(&sentence, DELIM);
396         if(token && *token)
397             _gps_sat[satcnt].elevation = atoi(token);
398
399         /* Get token for azimuth in degrees to true north (0-359). */
400         token = strsep(&sentence, DELIM);
401         if(token && *token)
402             _gps_sat[satcnt].azimuth = atoi(token);
403
404         /* Get token for SNR. */
405         token = strsep(&sentence, DELIM);
406         if(token && *token && (_gps_sat[satcnt].snr = atoi(token)))
407         {
408             /* SNR is non-zero - add to total and count as used. */
409             running_total += _gps_sat[satcnt].snr;
410             num_sats_used++;
411         }
412         satcnt++;
413     }
414
415     if(msgcnt == nummsgs)
416     {
417         /*  This is the last message. Calculate signal strength. */
418         if(num_sats_used)
419         {
420             if(_gps_state == RCVR_UP)
421             {
422                 gdouble fraction = running_total * sqrtf(num_sats_used)
423                     / num_sats_used / 150.0;
424                 BOUND(fraction, 0.0, 1.0);
425                 hildon_banner_set_fraction(
426                         HILDON_BANNER(_fix_banner), fraction);
427
428                 /* Keep awake while they watch the progress bar. */
429                 UNBLANK_SCREEN(TRUE, FALSE);
430             }
431             running_total = 0;
432             num_sats_used = 0;
433         }
434         satcnt = 0;
435     }
436
437     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
438 }
439
440 static gboolean
441 gps_handle_error_idle(gchar *error)
442 {
443     printf("%s(%s)\n", __PRETTY_FUNCTION__, error);
444
445     /* Ask for re-try. */
446     if(++_gps_rcvr_retry_count > 2)
447     {
448         GtkWidget *confirm;
449         gchar buffer[BUFFER_SIZE];
450
451         /* Reset retry count. */
452         _gps_rcvr_retry_count = 0;
453
454         snprintf(buffer, sizeof(buffer), "%s\nRetry?", error);
455         confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer);
456
457         rcvr_disconnect();
458
459         if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
460             rcvr_connect(); /* Try again. */
461         else
462         {
463             /* Disable GPS. */
464             gtk_check_menu_item_set_active(
465                     GTK_CHECK_MENU_ITEM(_menu_enable_gps_item), FALSE);
466         }
467
468         /* Ask user to re-connect. */
469         gtk_widget_destroy(confirm);
470     }
471     else
472     {
473         rcvr_disconnect();
474         rcvr_connect(); /* Try again. */
475     }
476
477     g_free(error);
478
479     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
480     return FALSE;
481 }
482
483 static gboolean
484 gps_parse_nmea_idle(gchar *nmea)
485 {
486     printf("%s(%s)\n", __PRETTY_FUNCTION__, nmea);
487
488     if(_enable_gps && _gps_state >= RCVR_DOWN)
489     {
490         if(_gps_state < RCVR_UP)
491             set_conn_state(RCVR_UP);
492
493         if(!strncmp(nmea + 3, "GSV", 3))
494         {
495             if(_gps_state == RCVR_UP || _gps_info || _satdetails_on)
496                 gps_parse_gsv(nmea + 7);
497         }
498         else if(!strncmp(nmea + 3, "RMC", 3))
499             gps_parse_rmc(nmea + 7);
500         else if(!strncmp(nmea + 3, "GGA", 3))
501             gps_parse_gga(nmea + 7);
502         else if(!strncmp(nmea + 3, "GSA", 3))
503             gps_parse_gsa(nmea + 7);
504
505         if(_gps_info)
506             gps_display_data();
507         if(_satdetails_on)
508             gps_display_details();
509     }
510
511     g_free(nmea);
512
513     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
514     return FALSE;
515 }
516
517 static GpsRcvrInfo*
518 gri_clone(GpsRcvrInfo *gri)
519 {
520     GpsRcvrInfo *ret = g_new(GpsRcvrInfo, 1);
521     ret->type = gri->type;
522     ret->bt_mac = g_strdup(gri->bt_mac);
523     ret->file_path = g_strdup(gri->file_path);
524     ret->gpsd_host = g_strdup(gri->gpsd_host);
525     ret->gpsd_port = gri->gpsd_port;
526     return ret;
527 }
528
529 static void
530 gri_free(GpsRcvrInfo *gri)
531 {
532     g_free(gri->bt_mac);
533     g_free(gri->file_path);
534     g_free(gri->gpsd_host);
535     g_free(gri);
536 }
537
538 static void
539 thread_read_nmea(GpsRcvrInfo *gri)
540 {
541     gchar buf[BUFFER_SIZE];
542     gchar *buf_curr = buf;
543     gchar *buf_last = buf + sizeof(buf) - 1;
544     GnomeVFSFileSize bytes_read;
545     GnomeVFSResult vfs_result;
546     gchar *gpsd_host = NULL;
547     gint gpsd_port = 0;
548     GnomeVFSInetConnection *iconn = NULL;
549     GnomeVFSSocket *socket = NULL;
550     GThread *my_thread = g_thread_self();
551     gboolean error = FALSE;
552     gpsbt_t gps_context;
553     gboolean is_context = FALSE;
554
555     printf("%s(%d)\n", __PRETTY_FUNCTION__, gri->type);
556
557     /* Lock/Unlock the mutex to ensure that _gps_thread is done being set. */
558     g_mutex_lock(_gps_init_mutex);
559     g_mutex_unlock(_gps_init_mutex);
560
561     switch(gri->type)
562     {
563         case GPS_RCVR_BT:
564         {
565             gchar errstr[BUFFER_SIZE] = "";
566             /* We need to start gpsd (via gpsbt) first. */
567             memset(&gps_context, 0, sizeof(gps_context));
568             errno = 0;
569             if(gpsbt_start(gri->bt_mac, 0, 0, 0, errstr, sizeof(errstr),
570                         0, &gps_context) < 0)
571             {
572                 g_printerr("Error connecting to GPS receiver: (%d) %s (%s)\n",
573                         errno, strerror(errno), errstr);
574                 g_idle_add((GSourceFunc)gps_handle_error_idle,
575                         g_strdup_printf("%s",
576                         _("Error connecting to GPS receiver.")));
577                 error = TRUE;
578             }
579             else
580             {
581                 /* Success.  Set gpsd_host and gpsd_port. */
582                 gpsd_host = "127.0.0.1";
583                 gpsd_port = GPSD_PORT_DEFAULT;
584                 is_context = TRUE;
585             }
586             break;
587         }
588         case GPS_RCVR_GPSD:
589         {
590             /* Set gpsd_host and gpsd_port. */
591             gpsd_host = gri->gpsd_host;
592             gpsd_port = gri->gpsd_port;
593             break;
594         }
595         case GPS_RCVR_FILE:
596         {
597             /* Use gpsmgr to create a GPSD that uses the file. */
598             gchar *gpsd_prog;
599             gchar *gpsd_ctrl_sock;
600             gchar *devs[2];
601             devs[0] = gri->file_path;
602             devs[1] = NULL;
603
604             /* Caller can override the name of the gpsd program and
605              * the used control socket. */
606             gpsd_prog = getenv("GPSD_PROG");
607             gpsd_ctrl_sock = getenv("GPSD_CTRL_SOCK");
608
609             if (!gpsd_prog)
610                 gpsd_prog = "gpsd";
611
612             memset(&gps_context, 0, sizeof(gps_context));
613             errno = 0;
614             if(gpsmgr_start(gpsd_prog, devs, gpsd_ctrl_sock,
615                         0, 0, &gps_context.mgr) < 0)
616             {
617                 g_printerr("Error opening GPS device: (%d) %s\n",
618                         errno, strerror(errno));
619                 g_idle_add((GSourceFunc)gps_handle_error_idle,
620                         g_strdup_printf("%s",
621                         _("Error opening GPS device.")));
622                 error = TRUE;
623             }
624             else
625             {
626                 /* Success.  Set gpsd_host and gpsd_port. */
627                 gpsd_host = "127.0.0.1";
628                 gpsd_port = GPSD_PORT_DEFAULT;
629                 is_context = TRUE;
630             }
631             break;
632         }
633         default:
634             error = TRUE;
635     }
636
637     if(!error && my_thread == _gps_thread)
638     {
639         gint tryno;
640
641         /* Attempt to connect to GPSD. */
642         for(tryno = 0; tryno < 10; tryno++)
643         {
644             /* Create a socket to interact with GPSD. */
645             GTimeVal timeout = { 10, 0 };
646
647             if(GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create(
648                             &iconn,
649                             gri->type == GPS_RCVR_GPSD ? gri->gpsd_host
650                                                        : "127.0.0.1",
651                             gri->type == GPS_RCVR_GPSD ? gri->gpsd_port
652                                                        : GPSD_PORT_DEFAULT,
653                             NULL))
654                || NULL == (socket = gnome_vfs_inet_connection_to_socket(iconn))
655                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout(
656                        socket, &timeout, NULL))
657                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_write(socket,
658                        "r+\r\n", sizeof("r+\r\n"), &bytes_read, NULL))
659                || bytes_read != sizeof("r+\r\n"))
660             {
661                 sleep(1);
662             }
663             else
664                 break;
665         }
666
667         if(!iconn)
668         {
669             g_printerr("Error connecting to GPSD server: (%d) %s\n",
670                     vfs_result, gnome_vfs_result_to_string(vfs_result));
671             g_idle_add((GSourceFunc)gps_handle_error_idle,
672                     g_strdup_printf("%s",
673                     _("Error connecting to GPSD server.")));
674             error = TRUE;
675         }
676     }
677
678     if(!error && my_thread == _gps_thread)
679     {
680         while(my_thread == _gps_thread)
681         {
682             gchar *eol;
683
684             vfs_result = gnome_vfs_socket_read( 
685                     socket,
686                     buf,
687                     buf_last - buf_curr,
688                     &bytes_read,
689                     NULL);
690
691             if(vfs_result != GNOME_VFS_OK)
692             {
693                 if(my_thread == _gps_thread)
694                 {
695                     /* Error wasn't user-initiated. */
696                     g_idle_add((GSourceFunc)gps_handle_error_idle,
697                             g_strdup_printf("%s",
698                                 _("Error reading GPS data.")));
699                     error = TRUE;
700                 }
701                 break;
702             }
703
704             /* Loop through the buffer and read each NMEA sentence. */
705             buf_curr += bytes_read;
706             *buf_curr = '\0'; /* append a \0 so we can read as string */
707             while(my_thread == _gps_thread && (eol = strchr(buf, '\n')))
708             {
709                 gint csum = 0;
710                 if(*buf == '$')
711                 {
712                     gchar *sptr = buf + 1; /* Skip the $ */
713                     /* This is the beginning of a sentence; okay to parse. */
714                     *eol = '\0'; /* overwrite \n with \0 */
715                     while(*sptr && *sptr != '*')
716                         csum ^= *sptr++;
717
718                     /* If we're at a \0 (meaning there is no checksum), or if
719                      * the checksum is good, then parse the sentence. */
720                     if(!*sptr || csum == strtol(sptr + 1, NULL, 16))
721                     {
722                         if(*sptr)
723                             *sptr = '\0'; /* take checksum out of the buffer.*/
724                         if(my_thread == _gps_thread)
725                             g_idle_add((GSourceFunc)gps_parse_nmea_idle,
726                                     g_strdup(buf));
727                     }
728                     else
729                     {
730                         /* There was a checksum, and it was bad. */
731                         g_printerr("%s: Bad checksum in NMEA sentence:\n%s\n",
732                                 __PRETTY_FUNCTION__, buf);
733                     }
734                 }
735
736                 /* If eol is at or after (buf_curr - 1) */
737                 if(eol >= (buf_curr - 1))
738                 {
739                     /* Last read was a newline - reset read buffer */
740                     buf_curr = buf;
741                     *buf_curr = '\0';
742                 }
743                 else
744                 {
745                     /* Move the next line to the front of the buffer. */
746                     memmove(buf, eol + 1,
747                             buf_curr - eol); /* include terminating 0 */
748                     /* Subtract _curr so that it's pointing at the new \0. */
749                     buf_curr -= (eol - buf + 1);
750                 }
751             }
752             _gps_rcvr_retry_count = 0;
753         }
754     }
755
756     /* Error, or we're done reading GPS. */
757
758     /* Clean up. */
759     if(iconn)
760         gnome_vfs_inet_connection_free(iconn, NULL);
761
762     if(is_context)
763     {
764         switch(gri->type)
765         {
766             case GPS_RCVR_BT:
767                 gpsbt_stop(&gps_context);
768                 break;
769
770             case GPS_RCVR_FILE:
771                 gpsmgr_stop(&gps_context.mgr);
772                 break;
773
774             default:
775                 ;
776         }
777     }
778
779     gri_free(gri);
780
781     printf("%s(): return\n", __PRETTY_FUNCTION__);
782     return;
783 }
784
785 /**
786  * Set the connection state.  This function controls all connection-related
787  * banners.
788  */
789 void
790 set_conn_state(ConnState new_conn_state)
791 {
792     printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
793
794     switch(_gps_state = new_conn_state)
795     {
796         case RCVR_OFF:
797         case RCVR_FIXED:
798             if(_connect_banner)
799             {
800                 gtk_widget_destroy(_connect_banner);
801                 _connect_banner = NULL;
802             }
803             if(_fix_banner)
804             {
805                 gtk_widget_destroy(_fix_banner);
806                 _fix_banner = NULL;
807             }
808             break;
809         case RCVR_DOWN:
810             if(_fix_banner)
811             {
812                 gtk_widget_destroy(_fix_banner);
813                 _fix_banner = NULL;
814             }
815             if(!_connect_banner)
816                 _connect_banner = hildon_banner_show_animation(
817                         _window, NULL, _("Searching for GPS receiver"));
818             break;
819         case RCVR_UP:
820             if(_connect_banner)
821             {
822                 gtk_widget_destroy(_connect_banner);
823                 _connect_banner = NULL;
824             }
825             if(!_fix_banner)
826             {
827                 _fix_banner = hildon_banner_show_progress(
828                         _window, NULL, _("Establishing GPS fix"));
829             }
830             break;
831         default: ; /* to quell warning. */
832     }
833     map_force_redraw();
834
835     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
836 }
837
838 /**
839  * Disconnect from the receiver.  This method cleans up any and everything
840  * that might be associated with the receiver.
841  */
842 void
843 rcvr_disconnect()
844 {
845     printf("%s()\n", __PRETTY_FUNCTION__);
846
847     _gps_thread = NULL;
848
849     if(_window)
850         set_conn_state(RCVR_OFF);
851     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
852 }
853
854 /**
855  * Connect to the receiver.
856  * This method assumes that _fd is -1 and _channel is NULL.  If unsure, call
857  * rcvr_disconnect() first.
858  * Since this is an idle function, this function returns whether or not it
859  * should be called again, which is always FALSE.
860  */
861 gboolean
862 rcvr_connect()
863 {
864     printf("%s(%d)\n", __PRETTY_FUNCTION__, _gps_state);
865
866     if(_enable_gps && _gps_state == RCVR_OFF)
867     {
868         set_conn_state(RCVR_DOWN);
869
870         /* Lock/Unlock the mutex to ensure that the thread doesn't
871          * start until _gps_thread is set. */
872         g_mutex_lock(_gps_init_mutex);
873         _gps_thread = g_thread_create((GThreadFunc)thread_read_nmea,
874                 gri_clone(&_gri), TRUE, NULL); /* Joinable. */
875         g_mutex_unlock(_gps_init_mutex);
876     }
877
878     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
879     return FALSE;
880 }
881
882 void
883 reset_bluetooth()
884 {
885     printf("%s()\n", __PRETTY_FUNCTION__);
886     if(system("/usr/bin/sudo -l | grep -q '/usr/sbin/hciconfig  *hci0  *reset'"
887             " && sudo /usr/sbin/hciconfig hci0 reset"))
888         popup_error(_window,
889                 _("An error occurred while trying to reset the bluetooth "
890                 "radio.\n\n"
891                 "Did you make sure to modify\nthe /etc/sudoers file?"));
892     else if(_gps_state != RCVR_OFF)
893     {
894         rcvr_connect();
895     }
896     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
897 }
898
899 void
900 gps_init()
901 {
902     printf("%s()\n", __PRETTY_FUNCTION__);
903
904     _gps_init_mutex = g_mutex_new();
905
906     /* Fix a stupid PATH bug in libgps. */
907     {
908         gchar *path_env = getenv("PATH");
909         gchar *new_path = g_strdup_printf("%s:%s", path_env, "/usr/sbin");
910         setenv("PATH", new_path, 1);
911         g_free(new_path);
912     }
913
914     /* set _gpsoffset */
915     {   
916         time_t time1;
917         struct tm time2;
918         time1 = time(NULL);
919         localtime_r(&time1, &time2);
920         _gmtoffset = time2.tm_gmtoff;
921     }
922
923     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
924 }
925
926 void
927 gps_destroy(gboolean last)
928 {
929     static GThread* tmp = NULL;
930     printf("%s()\n", __PRETTY_FUNCTION__);
931
932     if(!last)
933     {
934         if(_gps_thread)
935         {
936             tmp = (GThread*)_gps_thread;
937             _gps_thread = NULL;
938         }
939     }
940     else if(tmp)
941         g_thread_join(tmp);
942
943     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
944 }
945