]> git.itanic.dy.fi Git - maemo-mapper/blob - src/gps.c
* Fixed bug in editing POI categories (closes #2281).
[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 = "/usr/sbin/gpsd";
611             if (!gpsd_ctrl_sock)
612                 gpsd_ctrl_sock = "/tmp/.gpsd_ctrl_sock";
613
614             memset(&gps_context, 0, sizeof(gps_context));
615             errno = 0;
616             if(gpsmgr_start(gpsd_prog, devs, gpsd_ctrl_sock,
617                         0, 0, &gps_context.mgr) < 0)
618             {
619                 g_printerr("Error opening GPS device: (%d) %s\n",
620                         errno, strerror(errno));
621                 g_idle_add((GSourceFunc)gps_handle_error_idle,
622                         g_strdup_printf("%s",
623                         _("Error opening GPS device.")));
624                 error = TRUE;
625             }
626             else
627             {
628                 /* Success.  Set gpsd_host and gpsd_port. */
629                 gpsd_host = "127.0.0.1";
630                 gpsd_port = GPSD_PORT_DEFAULT;
631                 is_context = TRUE;
632             }
633             break;
634         }
635         default:
636             error = TRUE;
637     }
638
639     if(!error && my_thread == _gps_thread)
640     {
641         gint tryno;
642
643         /* Attempt to connect to GPSD. */
644         for(tryno = 0; tryno < 10; tryno++)
645         {
646             /* Create a socket to interact with GPSD. */
647             GTimeVal timeout = { 10, 0 };
648
649             if(GNOME_VFS_OK != (vfs_result = gnome_vfs_inet_connection_create(
650                             &iconn,
651                             gri->type == GPS_RCVR_GPSD ? gri->gpsd_host
652                                                        : "127.0.0.1",
653                             gri->type == GPS_RCVR_GPSD ? gri->gpsd_port
654                                                        : GPSD_PORT_DEFAULT,
655                             NULL))
656                || NULL == (socket = gnome_vfs_inet_connection_to_socket(iconn))
657                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_set_timeout(
658                        socket, &timeout, NULL))
659                || GNOME_VFS_OK != (vfs_result = gnome_vfs_socket_write(socket,
660                        "r+\r\n", sizeof("r+\r\n"), &bytes_read, NULL))
661                || bytes_read != sizeof("r+\r\n"))
662             {
663                 sleep(1);
664             }
665             else
666                 break;
667         }
668
669         if(!iconn)
670         {
671             g_printerr("Error connecting to GPSD server: (%d) %s\n",
672                     vfs_result, gnome_vfs_result_to_string(vfs_result));
673             g_idle_add((GSourceFunc)gps_handle_error_idle,
674                     g_strdup_printf("%s",
675                     _("Error connecting to GPSD server.")));
676             error = TRUE;
677         }
678     }
679
680     if(!error && my_thread == _gps_thread)
681     {
682         while(my_thread == _gps_thread)
683         {
684             gchar *eol;
685
686             vfs_result = gnome_vfs_socket_read( 
687                     socket,
688                     buf,
689                     buf_last - buf_curr,
690                     &bytes_read,
691                     NULL);
692
693             if(vfs_result != GNOME_VFS_OK)
694             {
695                 if(my_thread == _gps_thread)
696                 {
697                     /* Error wasn't user-initiated. */
698                     g_idle_add((GSourceFunc)gps_handle_error_idle,
699                             g_strdup_printf("%s",
700                                 _("Error reading GPS data.")));
701                     error = TRUE;
702                 }
703                 break;
704             }
705
706             /* Loop through the buffer and read each NMEA sentence. */
707             buf_curr += bytes_read;
708             *buf_curr = '\0'; /* append a \0 so we can read as string */
709             while(my_thread == _gps_thread && (eol = strchr(buf, '\n')))
710             {
711                 gint csum = 0;
712                 if(*buf == '$')
713                 {
714                     gchar *sptr = buf + 1; /* Skip the $ */
715                     /* This is the beginning of a sentence; okay to parse. */
716                     *eol = '\0'; /* overwrite \n with \0 */
717                     while(*sptr && *sptr != '*')
718                         csum ^= *sptr++;
719
720                     /* If we're at a \0 (meaning there is no checksum), or if
721                      * the checksum is good, then parse the sentence. */
722                     if(!*sptr || csum == strtol(sptr + 1, NULL, 16))
723                     {
724                         if(*sptr)
725                             *sptr = '\0'; /* take checksum out of the buffer.*/
726                         if(my_thread == _gps_thread)
727                             g_idle_add((GSourceFunc)gps_parse_nmea_idle,
728                                     g_strdup(buf));
729                     }
730                     else
731                     {
732                         /* There was a checksum, and it was bad. */
733                         g_printerr("%s: Bad checksum in NMEA sentence:\n%s\n",
734                                 __PRETTY_FUNCTION__, buf);
735                     }
736                 }
737
738                 /* If eol is at or after (buf_curr - 1) */
739                 if(eol >= (buf_curr - 1))
740                 {
741                     /* Last read was a newline - reset read buffer */
742                     buf_curr = buf;
743                     *buf_curr = '\0';
744                 }
745                 else
746                 {
747                     /* Move the next line to the front of the buffer. */
748                     memmove(buf, eol + 1,
749                             buf_curr - eol); /* include terminating 0 */
750                     /* Subtract _curr so that it's pointing at the new \0. */
751                     buf_curr -= (eol - buf + 1);
752                 }
753             }
754             _gps_rcvr_retry_count = 0;
755         }
756     }
757
758     /* Error, or we're done reading GPS. */
759
760     /* Clean up. */
761     if(iconn)
762         gnome_vfs_inet_connection_free(iconn, NULL);
763
764     if(is_context)
765     {
766         switch(gri->type)
767         {
768             case GPS_RCVR_BT:
769                 gpsbt_stop(&gps_context);
770                 break;
771
772             case GPS_RCVR_FILE:
773                 gpsmgr_stop(&gps_context.mgr);
774                 break;
775
776             default:
777                 ;
778         }
779     }
780
781     gri_free(gri);
782
783     printf("%s(): return\n", __PRETTY_FUNCTION__);
784     return;
785 }
786
787 /**
788  * Set the connection state.  This function controls all connection-related
789  * banners.
790  */
791 void
792 set_conn_state(ConnState new_conn_state)
793 {
794     printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state);
795
796     switch(_gps_state = new_conn_state)
797     {
798         case RCVR_OFF:
799         case RCVR_FIXED:
800             if(_connect_banner)
801             {
802                 gtk_widget_destroy(_connect_banner);
803                 _connect_banner = NULL;
804             }
805             if(_fix_banner)
806             {
807                 gtk_widget_destroy(_fix_banner);
808                 _fix_banner = NULL;
809             }
810             break;
811         case RCVR_DOWN:
812             if(_fix_banner)
813             {
814                 gtk_widget_destroy(_fix_banner);
815                 _fix_banner = NULL;
816             }
817             if(!_connect_banner)
818                 _connect_banner = hildon_banner_show_animation(
819                         _window, NULL, _("Searching for GPS receiver"));
820             break;
821         case RCVR_UP:
822             if(_connect_banner)
823             {
824                 gtk_widget_destroy(_connect_banner);
825                 _connect_banner = NULL;
826             }
827             if(!_fix_banner)
828             {
829                 _fix_banner = hildon_banner_show_progress(
830                         _window, NULL, _("Establishing GPS fix"));
831             }
832             break;
833         default: ; /* to quell warning. */
834     }
835     map_force_redraw();
836
837     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
838 }
839
840 /**
841  * Disconnect from the receiver.  This method cleans up any and everything
842  * that might be associated with the receiver.
843  */
844 void
845 rcvr_disconnect()
846 {
847     printf("%s()\n", __PRETTY_FUNCTION__);
848
849     _gps_thread = NULL;
850
851     if(_window)
852         set_conn_state(RCVR_OFF);
853     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
854 }
855
856 /**
857  * Connect to the receiver.
858  * This method assumes that _fd is -1 and _channel is NULL.  If unsure, call
859  * rcvr_disconnect() first.
860  * Since this is an idle function, this function returns whether or not it
861  * should be called again, which is always FALSE.
862  */
863 gboolean
864 rcvr_connect()
865 {
866     printf("%s(%d)\n", __PRETTY_FUNCTION__, _gps_state);
867
868     if(_enable_gps && _gps_state == RCVR_OFF)
869     {
870         set_conn_state(RCVR_DOWN);
871
872         /* Lock/Unlock the mutex to ensure that the thread doesn't
873          * start until _gps_thread is set. */
874         g_mutex_lock(_gps_init_mutex);
875         _gps_thread = g_thread_create((GThreadFunc)thread_read_nmea,
876                 gri_clone(&_gri), TRUE, NULL); /* Joinable. */
877         g_mutex_unlock(_gps_init_mutex);
878     }
879
880     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
881     return FALSE;
882 }
883
884 void
885 reset_bluetooth()
886 {
887     printf("%s()\n", __PRETTY_FUNCTION__);
888     if(system("/usr/bin/sudo -l | grep -q '/usr/sbin/hciconfig  *hci0  *reset'"
889             " && sudo /usr/sbin/hciconfig hci0 reset"))
890         popup_error(_window,
891                 _("An error occurred while trying to reset the bluetooth "
892                 "radio.\n\n"
893                 "Did you make sure to modify\nthe /etc/sudoers file?"));
894     else if(_gps_state != RCVR_OFF)
895     {
896         rcvr_connect();
897     }
898     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
899 }
900
901 void
902 gps_init()
903 {
904     printf("%s()\n", __PRETTY_FUNCTION__);
905
906     _gps_init_mutex = g_mutex_new();
907
908     /* Fix a stupid PATH bug in libgps. */
909     {
910         gchar *path_env = getenv("PATH");
911         gchar *new_path = g_strdup_printf("%s:%s", path_env, "/usr/sbin");
912         setenv("PATH", new_path, 1);
913         g_free(new_path);
914     }
915
916     /* set _gpsoffset */
917     {   
918         time_t time1;
919         struct tm time2;
920         time1 = time(NULL);
921         localtime_r(&time1, &time2);
922         _gmtoffset = time2.tm_gmtoff;
923     }
924
925     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
926 }
927
928 void
929 gps_destroy(gboolean last)
930 {
931     static GThread* tmp = NULL;
932     printf("%s()\n", __PRETTY_FUNCTION__);
933
934     if(!last)
935     {
936         if(_gps_thread)
937         {
938             tmp = (GThread*)_gps_thread;
939             _gps_thread = NULL;
940         }
941     }
942     else if(tmp)
943         g_thread_join(tmp);
944
945     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
946 }
947