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