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