8404945d7120cf95b18a454ad66d85a1eac5bcaa
[olsrd.git] / lib / pud / src / gpsdclient.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 #include "gpsdclient.h"
47
48 #include "configuration.h"
49
50 #include <nmealib/nmath.h>
51 #include <nmealib/sentence.h>
52 #include <errno.h>
53 #include <gps.h>
54 #include <math.h>
55 #include <string.h>
56 #include <syslog.h>
57
58 #if GPSD_API_MAJOR_VERSION < 6
59   #if GPSD_API_MINOR_VERSION < 1
60     #define GPSD_WHEEZY
61   #else
62     #define GPSD_JESSIE
63   #endif
64 #else
65   #define GPSD_NEW
66 #endif
67
68 /*
69  * Loggers
70  */
71
72 /* log gpsd log messages*/
73 static void gpsdLog(const char *s) {
74   syslog(LOG_INFO, "gpsd log: %s", s);
75 }
76
77 /* log gpsd errors */
78 static void gpsdError(const char *s) {
79   syslog(LOG_ERR, "gpsd error: %s", s);
80 }
81
82 /* standard parsing of a GPS data source spec */
83 void gpsdParseSourceSpec(char *arg, GpsDaemon *gpsDaemon) {
84   if (!arg //
85       || !gpsDaemon) {
86     return;
87   }
88
89   gpsDaemon->source.server = (char *) (unsigned long) DEFAULT_GPSD_HOST;
90   gpsDaemon->source.port = (char *) (unsigned long) DEFAULT_GPSD_PORT;
91   gpsDaemon->source.device = NULL;
92
93   if (*arg) {
94     char *colon1;
95     const char *skipto;
96     char *rbrk;
97
98     strncpy(gpsDaemon->source.spec, arg, PATH_MAX);
99     gpsDaemon->source.spec[PATH_MAX - 1] = '\0';
100
101     skipto = gpsDaemon->source.spec;
102     if ((*skipto == '[') //
103         && ((rbrk = strchr(skipto, ']')) != NULL )) {
104       skipto = rbrk;
105     }
106     colon1 = strchr(skipto, ':');
107
108     if (colon1 != NULL) {
109       char *colon2;
110
111       *colon1 = '\0';
112       if (colon1 != gpsDaemon->source.spec) {
113         gpsDaemon->source.server = gpsDaemon->source.spec;
114       }
115
116       gpsDaemon->source.port = colon1 + 1;
117       colon2 = strchr(gpsDaemon->source.port, ':');
118       if (colon2 != NULL) {
119         *colon2 = '\0';
120         gpsDaemon->source.device = colon2 + 1;
121       }
122     } else if (strchr(gpsDaemon->source.spec, '/') != NULL) {
123       gpsDaemon->source.device = gpsDaemon->source.spec;
124     } else {
125       gpsDaemon->source.server = gpsDaemon->source.spec;
126     }
127   }
128
129   if (*gpsDaemon->source.server == '[') {
130     char *rbrk = strchr(gpsDaemon->source.server, ']');
131     ++gpsDaemon->source.server;
132     if (rbrk != NULL) {
133       *rbrk = '\0';
134     }
135   }
136 }
137
138 bool gpsdConnect(GpsDaemon *gpsd, struct gps_data_t *gpsData, struct GpsdConnectionState *connectionTracking) {
139   bool disconnectFirst;
140
141   if (!gpsd //
142       || !gpsData //
143       || !connectionTracking) {
144     return false;
145   }
146
147   disconnectFirst = connectionTracking->connected;
148
149   if (disconnectFirst) {
150     gpsdDisconnect(gpsData, connectionTracking);
151   }
152
153   gpsData->gps_fd = -1;
154   if (gps_open(gpsd->source.server, gpsd->source.port, gpsData)) {
155     if (!connectionTracking->connectionFailureReported) {
156       syslog(LOG_WARNING, "%sonnecting to gpsd on %s:%s failed, starting retries", disconnectFirst ? "Rec" : "C", gpsd->source.server, gpsd->source.port);
157       connectionTracking->connectionFailureReported = true;
158     }
159     connectionTracking->retryCount++;
160     return false;
161   }
162
163   if (connectionTracking->connectionFailureReported) {
164     syslog(LOG_WARNING, "%sonnecting to gpsd on %s:%s successful after %llu retr%s", disconnectFirst ? "Rec" : "C", gpsd->source.server, gpsd->source.port,
165         connectionTracking->retryCount, connectionTracking->retryCount == 1 ? "y" : "ies");
166     connectionTracking->connectionFailureReported = false;
167   }
168   connectionTracking->retryCount = 0;
169
170   if (gpsd->source.device) {
171     gpsd->gpsdStreamFlags |= WATCH_DEVICE;
172   }
173
174   /* instruct gpsd which data to receive */
175   gps_stream(gpsData, gpsd->gpsdStreamFlags, gpsd->source.device);
176
177   return true;
178 }
179
180 void gpsdDisconnect(struct gps_data_t *gpsdata, struct GpsdConnectionState *connectionTracking) {
181   if (!gpsdata //
182       || !connectionTracking) {
183     return;
184   }
185
186   if (gpsdata->gps_fd >= 0) {
187     gps_close(gpsdata);
188     gpsdata->gps_fd = -1;
189     syslog(LOG_INFO, "Closed gpsd connection");
190   }
191
192   memset(connectionTracking, 0, sizeof(*connectionTracking));
193 }
194
195 /**
196  * Convert true heading to magnetic.  Taken from the Aviation
197  * Formulary v1.43.  Valid to within two degrees within the
198  * continiental USA except for the following airports: MO49 MO86 MO50
199  * 3K6 02K and KOOA.  AK correct to better than one degree.  Western
200  * Europe correct to within 0.2 deg.
201  *
202  * If you're not in one of these areas, I apologize, I don't have the
203  * math to compute your varation.  This is obviously extremely
204  * floating-point heavy, so embedded people, beware of using.
205  *
206  * Note that there are issues with using magnetic heading.  This code
207  * does not account for the possibility of travelling into or out of
208  * an area of valid calculation beyond forcing the magnetic conversion
209  * off.  A better way to communicate this to the user is probably
210  * desirable (in case the don't notice the subtle change from "(mag)"
211  * to "(true)" on their display).
212  */
213 float true2magnetic(double lat, double lon, double heading) {
214   /* Western Europe */
215   if ((lat > 36.0) && (lat < 68.0) && (lon > -10.0) && (lon < 28.0)) {
216     heading = (10.4768771667158 - (0.507385322418858 * lon) + (0.00753170031703826 * pow(lon, 2)) - (1.40596203924748e-05 * pow(lon, 3))
217         - (0.535560699962353 * lat) + (0.0154348808069955 * lat * lon) - (8.07756425110592e-05 * lat * pow(lon, 2)) + (0.00976887198864442 * pow(lat, 2))
218         - (0.000259163929798334 * lon * pow(lat, 2)) - (3.69056939266123e-05 * pow(lat, 3)) + heading);
219   }
220   /* USA */
221   else if ((lat > 24.0) && (lat < 50.0) && (lon > 66.0) && (lon < 125.0)) {
222     lon = 0.0 - lon;
223     heading = ((-65.6811) + (0.99 * lat) + (0.0128899 * pow(lat, 2)) - (0.0000905928 * pow(lat, 3)) + (2.87622 * lon) - (0.0116268 * lat * lon)
224         - (0.00000603925 * lon * pow(lat, 2)) - (0.0389806 * pow(lon, 2)) - (0.0000403488 * lat * pow(lon, 2)) + (0.000168556 * pow(lon, 3)) + heading);
225   }
226   /* AK */
227   else if ((lat > 54.0) && (lon > 130.0) && (lon < 172.0)) {
228     lon = 0.0 - lon;
229     heading = (618.854 + (2.76049 * lat) - (0.556206 * pow(lat, 2)) + (0.00251582 * pow(lat, 3)) - (12.7974 * lon) + (0.408161 * lat * lon)
230         + (0.000434097 * lon * pow(lat, 2)) - (0.00602173 * pow(lon, 2)) - (0.00144712 * lat * pow(lon, 2)) + (0.000222521 * pow(lon, 3)) + heading);
231   } else {
232     /* We don't know how to compute magnetic heading for this
233      * location. */
234     heading = NAN;
235   }
236
237   /* No negative headings. */
238   if (isnan(heading) == 0 && heading < 0.0) {
239     heading += 360.0;
240   }
241
242   return (float) (heading);
243 }
244
245 void nmeaInfoFromGpsd(struct gps_data_t *gpsdata, NmeaInfo *info, struct GpsdConnectionState *connectionTracking) {
246   if (!gpsdata //
247       || !info //
248       || !connectionTracking) {
249     return;
250   }
251
252   if (gpsdata->set & VERSION_SET) {
253     if (!connectionTracking->version) {
254       size_t releaseLength = strlen(gpsdata->version.release);
255       size_t revLength = strlen(gpsdata->version.rev);
256       size_t remoteLength = strlen(gpsdata->version.remote);
257       syslog(LOG_INFO, "Connected to gpsd%s%s%s%s%s%s on %s, protocol %d.%d", //
258           !releaseLength ? "" : " ", //
259           !releaseLength ? "" : gpsdata->version.release, //
260           !releaseLength ? "" : " ", //
261           !revLength ? "" : "(", //
262           !revLength ? "" : gpsdata->version.rev, //
263           !revLength ? "" : ")", //
264           !remoteLength ? "localhost" : gpsdata->version.remote, //
265           gpsdata->version.proto_major, //
266           gpsdata->version.proto_minor);
267
268       connectionTracking->version = true;
269     }
270
271     gpsdata->set &= ~VERSION_SET;
272   }
273
274   if (gpsdata->set & LOGMESSAGE_SET) {
275     gpsdLog(gpsdata->error);
276     gpsdata->set &= ~LOGMESSAGE_SET;
277   }
278
279   if (gpsdata->set & ERROR_SET) {
280     gpsdError(gpsdata->error);
281     gpsdata->set &= ~ERROR_SET;
282   }
283
284   if (gpsdata->set & (DEVICELIST_SET | DEVICE_SET | DEVICEID_SET)) {
285     int i = 0;
286     while (i < MAXUSERDEVS) {
287       struct devconfig_t *dev = &gpsdata->devices.list[i];
288       if (dev->flags //
289           && !connectionTracking->devSeen[i]) {
290         size_t subtypeLength = strlen(dev->subtype);
291         syslog(LOG_INFO, "Using %s device%s%s on %s in %s mode at %u baud (%u%c%u), refresh time %.3f (min. %.3f)", //
292             dev->driver, //
293             !subtypeLength ? "" : " ", //
294             !subtypeLength ? "" : dev->subtype, //
295             dev->path, //
296             dev->driver_mode ? "native" : "compatibility", //
297             dev->baudrate, //
298             8, //
299             dev->parity, //
300             dev->stopbits, //
301             dev->cycle, //
302             dev->mincycle);
303
304         connectionTracking->devSeen[i] = true;
305         connectionTracking->dev[i] = *dev;
306       } else if (connectionTracking->devSeen[i]) {
307         size_t subtypeLength;
308
309         dev = &connectionTracking->dev[i];
310         subtypeLength = strlen(dev->subtype);
311         syslog(LOG_INFO, "No longer using %s device%s%s on %s", //
312             dev->driver, //
313             !subtypeLength ? "" : " ", //
314             !subtypeLength ? "" : dev->subtype, //
315             dev->path);
316
317         connectionTracking->devSeen[i] = false;
318         memset(&connectionTracking->dev[i], 0, sizeof(connectionTracking->dev[i]));
319       }
320
321       i++;
322     }
323
324     gpsdata->set &= ~(DEVICELIST_SET | DEVICE_SET | DEVICEID_SET);
325   }
326
327   /* ignored */
328   gpsdata->set &= ~( //
329           ONLINE_SET // unreliable
330           | TIMERR_SET //
331           | CLIMB_SET //
332           | DOP_SET // using dop from fix
333           | ATTITUDE_SET //
334           | SPEEDERR_SET //
335           | TRACKERR_SET //
336           | CLIMBERR_SET //
337           | RTCM2_SET //
338           | RTCM3_SET //
339           | AIS_SET //
340           | PACKET_SET //
341           | SUBFRAME_SET //
342           | GST_SET //
343           | POLICY_SET //
344 #ifdef GPSD_JESSIE
345           | TIMEDRIFT_SET //
346           | EOF_SET //
347 #endif
348 #ifdef GPSD_NEW
349           | TOFF_SET //
350           | PPS_SET //
351           | NAVDATA_SET //
352 #endif
353           );
354
355   gpsdata->set &= ~STATUS_SET; /* always valid */
356   if (gpsdata->status == STATUS_NO_FIX) {
357     nmeaInfoClear(info);
358     return;
359   }
360
361   if (!gpsdata->set) {
362     return;
363   }
364
365   info->smask = NMEALIB_SENTENCE_MASK;
366   nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_SMASK);
367
368   /* date & time */
369   if (!isNaN(gpsdata->fix.time)) {
370     double seconds;
371     double fraction = modf(fabs(gpsdata->fix.time), &seconds);
372     long sec = lrint(seconds);
373     struct tm *time = gmtime(&sec);
374     if (time) {
375       info->utc.year = (unsigned int) time->tm_year + 1900;
376       info->utc.mon = (unsigned int) time->tm_mon + 1;
377       info->utc.day = (unsigned int) time->tm_mday;
378       info->utc.hour = (unsigned int) time->tm_hour;
379       info->utc.min = (unsigned int) time->tm_min;
380       info->utc.sec = (unsigned int) time->tm_sec;
381       info->utc.hsec = (unsigned int) lrint(fraction * 100);
382
383       nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_UTCDATE | NMEALIB_PRESENT_UTCTIME);
384     }
385   }
386   gpsdata->set &= ~TIME_SET;
387
388   /* sig & fix */
389   if (!gpsdata->online) {
390     gpsdata->fix.mode = MODE_NO_FIX;
391   }
392
393   switch (gpsdata->fix.mode) {
394     case MODE_3D:
395       info->fix = NMEALIB_FIX_3D;
396       info->sig = NMEALIB_SIG_FIX;
397       break;
398
399     case MODE_2D:
400       info->fix = NMEALIB_FIX_2D;
401       info->sig = NMEALIB_SIG_FIX;
402       break;
403
404     case MODE_NOT_SEEN:
405     case MODE_NO_FIX:
406     default:
407       info->fix = NMEALIB_FIX_BAD;
408       info->sig = NMEALIB_SIG_INVALID;
409       break;
410   }
411   nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_FIX | NMEALIB_PRESENT_SIG);
412   gpsdata->set &= ~MODE_SET;
413
414   if ((info->fix == NMEALIB_FIX_BAD) //
415       || (info->sig == NMEALIB_SIG_INVALID)) {
416     nmeaInfoClear(info);
417     nmeaTimeSet(&info->utc, &info->present, NULL);
418     return;
419   }
420
421   /* hdop */
422   if (!isNaN(gpsdata->fix.epx) //
423       && !isNaN(gpsdata->fix.epy)) {
424     info->hdop = nmeaMathPdopCalculate(gpsdata->fix.epx, gpsdata->fix.epy);
425     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_HDOP);
426   }
427   gpsdata->set &= ~HERR_SET;
428
429   /* vdop */
430   if (!isNaN(gpsdata->fix.epv)) {
431     info->vdop = gpsdata->fix.epv;
432     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_VDOP);
433   }
434   gpsdata->set &= ~VERR_SET;
435
436   /* pdop */
437   if (nmeaInfoIsPresentAll(info->present, NMEALIB_PRESENT_HDOP | NMEALIB_PRESENT_VDOP)) {
438     info->pdop = nmeaMathPdopCalculate(info->hdop, info->vdop);
439     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_PDOP);
440   }
441
442   /* lat */
443   if ((gpsdata->fix.mode >= MODE_2D) //
444       && !isNaN(gpsdata->fix.latitude)) {
445     info->latitude = nmeaMathDegreeToNdeg(gpsdata->fix.latitude);
446     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_LAT);
447   }
448
449   /* lon */
450   if ((gpsdata->fix.mode >= MODE_2D) //
451       && !isNaN(gpsdata->fix.longitude)) {
452     info->longitude = nmeaMathDegreeToNdeg(gpsdata->fix.longitude);
453     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_LON);
454   }
455
456   /* lat & lon */
457   gpsdata->set &= ~LATLON_SET;
458
459   /* elv */
460   if ((gpsdata->fix.mode >= MODE_3D) //
461       && !isNaN(gpsdata->fix.altitude)) {
462     info->elevation = gpsdata->fix.altitude;
463     info->height = gpsdata->separation;
464     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_ELV | NMEALIB_PRESENT_HEIGHT);
465   }
466   gpsdata->set &= ~ALTITUDE_SET;
467
468   /* speed */
469   if ((gpsdata->fix.mode >= MODE_2D) //
470       && !isNaN(gpsdata->fix.speed)) {
471     info->speed = gpsdata->fix.speed * MPS_TO_KPH;
472     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_SPEED);
473   }
474   gpsdata->set &= ~SPEED_SET;
475
476   /* track & mtrack */
477   if ((gpsdata->fix.mode >= MODE_2D) //
478       && !isNaN(gpsdata->fix.track)) {
479     double magheading;
480
481     info->track = gpsdata->fix.track;
482     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_TRACK);
483
484     magheading = true2magnetic(gpsdata->fix.latitude, gpsdata->fix.longitude, gpsdata->fix.track);
485     if (!isNaN(magheading)) {
486       info->mtrack = magheading;
487       nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_MTRACK);
488     }
489   }
490   gpsdata->set &= ~TRACK_SET;
491
492   /* magvar: not available */
493
494   /* satellites */
495
496   info->satellites.inUseCount = 0;
497   memset(&info->satellites.inUse, 0, sizeof(info->satellites.inUse));
498   info->satellites.inViewCount = 0;
499   memset(&info->satellites.inView, 0, sizeof(info->satellites.inView));
500
501   if (gpsdata->satellites_visible > 0) {
502     int iGpsd;
503
504 #ifndef GPSD_NEW
505     bool usedFlags[MAXCHANNELS];
506
507     memset(usedFlags, 0, sizeof(usedFlags));
508
509     /* build a bitmap of which satellites are used */
510     for (iGpsd = 0; iGpsd < MAXCHANNELS; iGpsd++) {
511       size_t iGpsdUsed;
512       for (iGpsdUsed = 0; iGpsdUsed < (size_t) gpsdata->satellites_used; iGpsdUsed++) {
513         if (gpsdata->used[iGpsdUsed] == gpsdata->PRN[iGpsd]) {
514           usedFlags[iGpsd] = true;
515           iGpsdUsed = (size_t) gpsdata->satellites_used;
516         }
517       }
518     }
519 #endif
520
521     for (iGpsd = 0; //
522         (iGpsd < gpsdata->satellites_visible) && (iGpsd < MAXCHANNELS) && (iGpsd < (int) NMEALIB_MAX_SATELLITES); //
523         iGpsd++) {
524       NmeaSatellite *infoSatellite = &info->satellites.inView[iGpsd];
525       unsigned int prn;
526       int elevation;
527       unsigned int azimuth;
528       unsigned int snr;
529       bool inUse;
530
531 #ifndef GPSD_NEW
532       prn = (unsigned int) gpsdata->PRN[iGpsd];
533       elevation = gpsdata->elevation[iGpsd];
534       azimuth = (unsigned int) gpsdata->azimuth[iGpsd];
535       snr = (unsigned int) lrint(gpsdata->ss[iGpsd]);
536       inUse = usedFlags[iGpsd];
537 #else
538       struct satellite_t *gpsdSatellite = &gpsdata->skyview[iGpsd];
539
540       prn = (unsigned int) gpsdSatellite->PRN;
541       elevation = gpsdSatellite->elevation;
542       azimuth = (unsigned int) gpsdSatellite->azimuth;
543       snr = (unsigned int) lrint(gpsdSatellite->ss);
544       inUse = gpsdSatellite->used;
545 #endif
546
547       infoSatellite->prn = prn;
548       infoSatellite->elevation = elevation;
549       infoSatellite->azimuth = azimuth;
550       infoSatellite->snr = snr;
551       info->satellites.inViewCount++;
552
553       if (inUse) {
554         info->satellites.inUse[iGpsd] = prn;
555         info->satellites.inUseCount++;
556       }
557     }
558   }
559   nmeaInfoSetPresent(&info->present, //
560       NMEALIB_PRESENT_SATINUSECOUNT //
561       | NMEALIB_PRESENT_SATINUSE //
562       | NMEALIB_PRESENT_SATINVIEWCOUNT //
563       | NMEALIB_PRESENT_SATINVIEW);
564   gpsdata->set &= ~SATELLITE_SET;
565
566   nmeaInfoSanitise(info);
567
568   if (gpsdata->set) {
569     syslog(LOG_WARNING, "Unhandled bits in gpsdata->set: %llx", (long long unsigned int) gpsdata->set);
570   }
571 }
572
573 void readFromGpsd(GpsDaemon *gpsd, struct gps_data_t *gpsdata, struct GpsdConnectionState *connectionTracking, NmeaInfo *nmeaInfo) {
574   int gpsReadCode;
575
576   if (!gpsd //
577       || !gpsdata //
578       || !connectionTracking //
579       || !nmeaInfo) {
580     return;
581   }
582
583   errno = 0;
584   if (!connectionTracking->connected) {
585     gpsReadCode = -1;
586   } else {
587     gpsReadCode = gps_read(gpsdata);
588   }
589
590   if (gpsReadCode > 0) {
591     /* data received from gpsd */
592     nmeaInfoFromGpsd(gpsdata, nmeaInfo, connectionTracking);
593   } else if (gpsReadCode < 0) {
594     /* failed to receive data from gpsd */
595
596     if (connectionTracking->connected) {
597       if (errno) {
598         syslog(LOG_WARNING, "Disconnected from gpsd: %s", strerror(errno));
599       } else {
600         syslog(LOG_WARNING, "Disconnected from gpsd");
601       }
602
603       gpsdDisconnect(gpsdata, connectionTracking);
604       nmeaInfoClear(nmeaInfo);
605     }
606
607     connectionTracking->connected = gpsdConnect(gpsd, gpsdata, connectionTracking);
608     nmeaTimeSet(&nmeaInfo->utc, &nmeaInfo->present, NULL);
609   } else {
610     /* no data received from gpsd */
611   }
612 }