Merge branch 'PolynomialDivision-feature/add_workflow'
[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 #if GPSD_API_MAJOR_VERSION >= 9
83 static double time_as_double(struct timespec *ts) {
84  return (ts->tv_sec + ts->tv_nsec * 1e-9);
85 }
86
87 static bool is_online(struct gps_data_t *gpsdata) {
88   return !!gpsdata->online.tv_sec;
89 }
90 #else
91
92 #define time_as_double(x) *(x)
93
94 static bool is_online(struct gps_data_t *gpsdata) {
95   return !!gpsdata->online;
96 }
97 #endif
98
99 /* standard parsing of a GPS data source spec */
100 void gpsdParseSourceSpec(char *arg, GpsDaemon *gpsDaemon) {
101   if (!arg //
102       || !gpsDaemon) {
103     return;
104   }
105
106   gpsDaemon->source.server = (char *) (unsigned long) DEFAULT_GPSD_HOST;
107   gpsDaemon->source.port = (char *) (unsigned long) DEFAULT_GPSD_PORT;
108   gpsDaemon->source.device = NULL;
109
110   if (*arg) {
111     char *colon1;
112     const char *skipto;
113     char *rbrk;
114
115     strncpy(gpsDaemon->source.spec, arg, PATH_MAX);
116     gpsDaemon->source.spec[PATH_MAX - 1] = '\0';
117
118     skipto = gpsDaemon->source.spec;
119     if ((*skipto == '[') //
120         && ((rbrk = strchr(skipto, ']')) != NULL )) {
121       skipto = rbrk;
122     }
123     colon1 = strchr(skipto, ':');
124
125     if (colon1 != NULL) {
126       char *colon2;
127
128       *colon1 = '\0';
129       if (colon1 != gpsDaemon->source.spec) {
130         gpsDaemon->source.server = gpsDaemon->source.spec;
131       }
132
133       gpsDaemon->source.port = colon1 + 1;
134       colon2 = strchr(gpsDaemon->source.port, ':');
135       if (colon2 != NULL) {
136         *colon2 = '\0';
137         gpsDaemon->source.device = colon2 + 1;
138       }
139     } else if (strchr(gpsDaemon->source.spec, '/') != NULL) {
140       gpsDaemon->source.device = gpsDaemon->source.spec;
141     } else {
142       gpsDaemon->source.server = gpsDaemon->source.spec;
143     }
144   }
145
146   if (*gpsDaemon->source.server == '[') {
147     char *rbrk = strchr(gpsDaemon->source.server, ']');
148     ++gpsDaemon->source.server;
149     if (rbrk != NULL) {
150       *rbrk = '\0';
151     }
152   }
153 }
154
155 bool gpsdConnect(GpsDaemon *gpsd, struct gps_data_t *gpsData, struct GpsdConnectionState *connectionTracking) {
156   bool disconnectFirst;
157
158   if (!gpsd //
159       || !gpsData //
160       || !connectionTracking) {
161     return false;
162   }
163
164   disconnectFirst = connectionTracking->connected;
165
166   if (disconnectFirst) {
167     gpsdDisconnect(gpsData, connectionTracking);
168   }
169
170   gpsData->gps_fd = -1;
171   if (gps_open(gpsd->source.server, gpsd->source.port, gpsData)) {
172     if (!connectionTracking->connectionFailureReported) {
173       syslog(LOG_WARNING, "%sonnecting to gpsd on %s:%s failed, starting retries", disconnectFirst ? "Rec" : "C", gpsd->source.server, gpsd->source.port);
174       connectionTracking->connectionFailureReported = true;
175     }
176     connectionTracking->retryCount++;
177     return false;
178   }
179
180   if (connectionTracking->connectionFailureReported) {
181     syslog(LOG_WARNING, "%sonnecting to gpsd on %s:%s successful after %llu retr%s", disconnectFirst ? "Rec" : "C", gpsd->source.server, gpsd->source.port,
182         connectionTracking->retryCount, connectionTracking->retryCount == 1 ? "y" : "ies");
183     connectionTracking->connectionFailureReported = false;
184   }
185   connectionTracking->retryCount = 0;
186
187   if (gpsd->source.device) {
188     gpsd->gpsdStreamFlags |= WATCH_DEVICE;
189   }
190
191   /* instruct gpsd which data to receive */
192   gps_stream(gpsData, gpsd->gpsdStreamFlags, gpsd->source.device);
193
194   return true;
195 }
196
197 void gpsdDisconnect(struct gps_data_t *gpsdata, struct GpsdConnectionState *connectionTracking) {
198   if (!gpsdata //
199       || !connectionTracking) {
200     return;
201   }
202
203   if (gpsdata->gps_fd >= 0) {
204     gps_close(gpsdata);
205     gpsdata->gps_fd = -1;
206     syslog(LOG_INFO, "Closed gpsd connection");
207   }
208
209   memset(connectionTracking, 0, sizeof(*connectionTracking));
210 }
211
212 /**
213  * Convert true heading to magnetic.  Taken from the Aviation
214  * Formulary v1.43.  Valid to within two degrees within the
215  * continiental USA except for the following airports: MO49 MO86 MO50
216  * 3K6 02K and KOOA.  AK correct to better than one degree.  Western
217  * Europe correct to within 0.2 deg.
218  *
219  * If you're not in one of these areas, I apologize, I don't have the
220  * math to compute your varation.  This is obviously extremely
221  * floating-point heavy, so embedded people, beware of using.
222  *
223  * Note that there are issues with using magnetic heading.  This code
224  * does not account for the possibility of travelling into or out of
225  * an area of valid calculation beyond forcing the magnetic conversion
226  * off.  A better way to communicate this to the user is probably
227  * desirable (in case the don't notice the subtle change from "(mag)"
228  * to "(true)" on their display).
229  */
230 float true2magnetic(double lat, double lon, double heading) {
231   /* Western Europe */
232   if ((lat > 36.0) && (lat < 68.0) && (lon > -10.0) && (lon < 28.0)) {
233     heading = (10.4768771667158 - (0.507385322418858 * lon) + (0.00753170031703826 * pow(lon, 2)) - (1.40596203924748e-05 * pow(lon, 3))
234         - (0.535560699962353 * lat) + (0.0154348808069955 * lat * lon) - (8.07756425110592e-05 * lat * pow(lon, 2)) + (0.00976887198864442 * pow(lat, 2))
235         - (0.000259163929798334 * lon * pow(lat, 2)) - (3.69056939266123e-05 * pow(lat, 3)) + heading);
236   }
237   /* USA */
238   else if ((lat > 24.0) && (lat < 50.0) && (lon > 66.0) && (lon < 125.0)) {
239     lon = 0.0 - lon;
240     heading = ((-65.6811) + (0.99 * lat) + (0.0128899 * pow(lat, 2)) - (0.0000905928 * pow(lat, 3)) + (2.87622 * lon) - (0.0116268 * lat * lon)
241         - (0.00000603925 * lon * pow(lat, 2)) - (0.0389806 * pow(lon, 2)) - (0.0000403488 * lat * pow(lon, 2)) + (0.000168556 * pow(lon, 3)) + heading);
242   }
243   /* AK */
244   else if ((lat > 54.0) && (lon > 130.0) && (lon < 172.0)) {
245     lon = 0.0 - lon;
246     heading = (618.854 + (2.76049 * lat) - (0.556206 * pow(lat, 2)) + (0.00251582 * pow(lat, 3)) - (12.7974 * lon) + (0.408161 * lat * lon)
247         + (0.000434097 * lon * pow(lat, 2)) - (0.00602173 * pow(lon, 2)) - (0.00144712 * lat * pow(lon, 2)) + (0.000222521 * pow(lon, 3)) + heading);
248   } else {
249     /* We don't know how to compute magnetic heading for this
250      * location. */
251     heading = NAN;
252   }
253
254   /* No negative headings. */
255   if (isnan(heading) == 0 && heading < 0.0) {
256     heading += 360.0;
257   }
258
259   return (float) (heading);
260 }
261
262 void nmeaInfoFromGpsd(struct gps_data_t *gpsdata, NmeaInfo *info, struct GpsdConnectionState *connectionTracking) {
263   if (!gpsdata //
264       || !info //
265       || !connectionTracking) {
266     return;
267   }
268
269   if (gpsdata->set & VERSION_SET) {
270     if (!connectionTracking->version) {
271       size_t releaseLength = strlen(gpsdata->version.release);
272       size_t revLength = strlen(gpsdata->version.rev);
273       size_t remoteLength = strlen(gpsdata->version.remote);
274       syslog(LOG_INFO, "Connected to gpsd%s%s%s%s%s%s on %s, protocol %d.%d", //
275           !releaseLength ? "" : " ", //
276           !releaseLength ? "" : gpsdata->version.release, //
277           !releaseLength ? "" : " ", //
278           !revLength ? "" : "(", //
279           !revLength ? "" : gpsdata->version.rev, //
280           !revLength ? "" : ")", //
281           !remoteLength ? "localhost" : gpsdata->version.remote, //
282           gpsdata->version.proto_major, //
283           gpsdata->version.proto_minor);
284
285       connectionTracking->version = true;
286     }
287
288     gpsdata->set &= ~VERSION_SET;
289   }
290
291   if (gpsdata->set & LOGMESSAGE_SET) {
292     gpsdLog(gpsdata->error);
293     gpsdata->set &= ~LOGMESSAGE_SET;
294   }
295
296   if (gpsdata->set & ERROR_SET) {
297     gpsdError(gpsdata->error);
298     gpsdata->set &= ~ERROR_SET;
299   }
300
301   if (gpsdata->set & (DEVICELIST_SET | DEVICE_SET | DEVICEID_SET)) {
302     int i = 0;
303     while (i < MAXUSERDEVS) {
304       struct devconfig_t *dev = &gpsdata->devices.list[i];
305       if (dev->flags //
306           && !connectionTracking->devSeen[i]) {
307         size_t subtypeLength = strlen(dev->subtype);
308         syslog(LOG_INFO, "Using %s device%s%s on %s in %s mode at %u baud (%u%c%u), refresh time %.3f (min. %.3f)", //
309             dev->driver, //
310             !subtypeLength ? "" : " ", //
311             !subtypeLength ? "" : dev->subtype, //
312             dev->path, //
313             dev->driver_mode ? "native" : "compatibility", //
314             dev->baudrate, //
315             8, //
316             dev->parity, //
317             dev->stopbits, //
318             time_as_double(&dev->cycle), //
319             time_as_double(&dev->mincycle));
320
321         connectionTracking->devSeen[i] = true;
322         connectionTracking->dev[i] = *dev;
323       } else if (connectionTracking->devSeen[i]) {
324         size_t subtypeLength;
325
326         dev = &connectionTracking->dev[i];
327         subtypeLength = strlen(dev->subtype);
328         syslog(LOG_INFO, "No longer using %s device%s%s on %s", //
329             dev->driver, //
330             !subtypeLength ? "" : " ", //
331             !subtypeLength ? "" : dev->subtype, //
332             dev->path);
333
334         connectionTracking->devSeen[i] = false;
335         memset(&connectionTracking->dev[i], 0, sizeof(connectionTracking->dev[i]));
336       }
337
338       i++;
339     }
340
341     gpsdata->set &= ~(DEVICELIST_SET | DEVICE_SET | DEVICEID_SET);
342   }
343
344   /* ignored */
345   gpsdata->set &= ~( //
346           ONLINE_SET // unreliable
347           | TIMERR_SET //
348           | CLIMB_SET //
349           | DOP_SET // using dop from fix
350           | ATTITUDE_SET //
351           | SPEEDERR_SET //
352           | TRACKERR_SET //
353           | CLIMBERR_SET //
354           | RTCM2_SET //
355           | RTCM3_SET //
356           | AIS_SET //
357           | PACKET_SET //
358           | SUBFRAME_SET //
359           | GST_SET //
360           | POLICY_SET //
361 #ifdef GPSD_JESSIE
362           | TIMEDRIFT_SET //
363           | EOF_SET //
364 #endif
365 #ifdef GPSD_NEW
366           | TOFF_SET //
367           | PPS_SET //
368           | NAVDATA_SET //
369 #endif
370           );
371
372   gpsdata->set &= ~STATUS_SET; /* always valid */
373   if (gpsdata->status == STATUS_NO_FIX) {
374     nmeaInfoClear(info);
375     nmeaTimeSet(&info->utc, &info->present, NULL);
376     return;
377   }
378
379   if (!gpsdata->set) {
380     return;
381   }
382
383   info->smask = NMEALIB_SENTENCE_MASK;
384   nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_SMASK);
385
386   /* date & time */
387 #if GPSD_API_MAJOR_VERSION >= 9
388   if (gpsdata->fix.time.tv_sec > 0) {
389     struct tm *time = gmtime(&gpsdata->fix.time.tv_sec);
390     unsigned int hsec = (unsigned int) (gpsdata->fix.time.tv_nsec / 10000000);
391 #else
392   if (!isNaN(gpsdata->fix.time)) {
393     double seconds;
394     double fraction = modf(fabs(gpsdata->fix.time), &seconds);
395     long sec = lrint(seconds);
396     struct tm *time = gmtime(&sec);
397     unsigned int hsec = (unsigned int) lrint(fraction * 100);
398 #endif
399     if (time) {
400       info->utc.year = (unsigned int) time->tm_year + 1900;
401       info->utc.mon = (unsigned int) time->tm_mon + 1;
402       info->utc.day = (unsigned int) time->tm_mday;
403       info->utc.hour = (unsigned int) time->tm_hour;
404       info->utc.min = (unsigned int) time->tm_min;
405       info->utc.sec = (unsigned int) time->tm_sec;
406       info->utc.hsec = hsec;
407
408       nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_UTCDATE | NMEALIB_PRESENT_UTCTIME);
409     }
410   }
411   gpsdata->set &= ~TIME_SET;
412
413   /* sig & fix */
414   if (!is_online(gpsdata)) {
415     gpsdata->fix.mode = MODE_NO_FIX;
416   }
417
418   switch (gpsdata->fix.mode) {
419     case MODE_3D:
420       info->fix = NMEALIB_FIX_3D;
421       info->sig = NMEALIB_SIG_FIX;
422       break;
423
424     case MODE_2D:
425       info->fix = NMEALIB_FIX_2D;
426       info->sig = NMEALIB_SIG_FIX;
427       break;
428
429     case MODE_NOT_SEEN:
430     case MODE_NO_FIX:
431     default:
432       info->fix = NMEALIB_FIX_BAD;
433       info->sig = NMEALIB_SIG_INVALID;
434       break;
435   }
436   nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_FIX | NMEALIB_PRESENT_SIG);
437   gpsdata->set &= ~MODE_SET;
438
439   /* hdop */
440   if (!isNaN(gpsdata->fix.epx) //
441       && !isNaN(gpsdata->fix.epy)) {
442     info->hdop = nmeaMathPdopCalculate(gpsdata->fix.epx, gpsdata->fix.epy);
443     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_HDOP);
444   }
445   gpsdata->set &= ~HERR_SET;
446
447   /* vdop */
448   if (!isNaN(gpsdata->fix.epv)) {
449     info->vdop = gpsdata->fix.epv;
450     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_VDOP);
451   }
452   gpsdata->set &= ~VERR_SET;
453
454   /* pdop */
455   if (nmeaInfoIsPresentAll(info->present, NMEALIB_PRESENT_HDOP | NMEALIB_PRESENT_VDOP)) {
456     info->pdop = nmeaMathPdopCalculate(info->hdop, info->vdop);
457     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_PDOP);
458   }
459
460   /* lat */
461   if ((gpsdata->fix.mode >= MODE_2D) //
462       && !isNaN(gpsdata->fix.latitude)) {
463     info->latitude = nmeaMathDegreeToNdeg(gpsdata->fix.latitude);
464     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_LAT);
465   }
466
467   /* lon */
468   if ((gpsdata->fix.mode >= MODE_2D) //
469       && !isNaN(gpsdata->fix.longitude)) {
470     info->longitude = nmeaMathDegreeToNdeg(gpsdata->fix.longitude);
471     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_LON);
472   }
473
474   /* lat & lon */
475   gpsdata->set &= ~LATLON_SET;
476
477   /* elv */
478   if ((gpsdata->fix.mode >= MODE_3D) //
479       && !isNaN(gpsdata->fix.altitude)) {
480     info->elevation = gpsdata->fix.altitude;
481 #if GPSD_API_MAJOR_VERSION >= 9
482     info->height = gpsdata->fix.geoid_sep;
483 #else
484     info->height = gpsdata->separation;
485 #endif
486     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_ELV | NMEALIB_PRESENT_HEIGHT);
487   }
488   gpsdata->set &= ~ALTITUDE_SET;
489
490   /* speed */
491   if ((gpsdata->fix.mode >= MODE_2D) //
492       && !isNaN(gpsdata->fix.speed)) {
493     info->speed = gpsdata->fix.speed * MPS_TO_KPH;
494     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_SPEED);
495   }
496   gpsdata->set &= ~SPEED_SET;
497
498   /* track & mtrack */
499   if ((gpsdata->fix.mode >= MODE_2D) //
500       && !isNaN(gpsdata->fix.track)) {
501     double magheading;
502
503     info->track = gpsdata->fix.track;
504     nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_TRACK);
505
506     magheading = true2magnetic(gpsdata->fix.latitude, gpsdata->fix.longitude, gpsdata->fix.track);
507     if (!isNaN(magheading)) {
508       info->mtrack = magheading;
509       nmeaInfoSetPresent(&info->present, NMEALIB_PRESENT_MTRACK);
510     }
511   }
512   gpsdata->set &= ~TRACK_SET;
513
514   /* magvar: not available */
515
516   /* satellites */
517
518   info->satellites.inUseCount = 0;
519   memset(&info->satellites.inUse, 0, sizeof(info->satellites.inUse));
520   info->satellites.inViewCount = 0;
521   memset(&info->satellites.inView, 0, sizeof(info->satellites.inView));
522
523   if (gpsdata->satellites_visible > 0) {
524     int iGpsd;
525
526 #ifndef GPSD_NEW
527     bool usedFlags[MAXCHANNELS];
528
529     memset(usedFlags, 0, sizeof(usedFlags));
530
531     /* build a bitmap of which satellites are used */
532     for (iGpsd = 0; iGpsd < MAXCHANNELS; iGpsd++) {
533       size_t iGpsdUsed;
534       for (iGpsdUsed = 0; iGpsdUsed < (size_t) gpsdata->satellites_used; iGpsdUsed++) {
535         if (gpsdata->used[iGpsdUsed] == gpsdata->PRN[iGpsd]) {
536           usedFlags[iGpsd] = true;
537           iGpsdUsed = (size_t) gpsdata->satellites_used;
538         }
539       }
540     }
541 #endif
542
543     for (iGpsd = 0; //
544         (iGpsd < gpsdata->satellites_visible) && (iGpsd < MAXCHANNELS) && (iGpsd < (int) NMEALIB_MAX_SATELLITES); //
545         iGpsd++) {
546       NmeaSatellite *infoSatellite = &info->satellites.inView[iGpsd];
547       unsigned int prn;
548       int elevation;
549       unsigned int azimuth;
550       unsigned int snr;
551       bool inUse;
552
553 #ifndef GPSD_NEW
554       prn = (unsigned int) gpsdata->PRN[iGpsd];
555       elevation = gpsdata->elevation[iGpsd];
556       azimuth = (unsigned int) gpsdata->azimuth[iGpsd];
557       snr = (unsigned int) lrint(gpsdata->ss[iGpsd]);
558       inUse = usedFlags[iGpsd];
559 #else
560       struct satellite_t *gpsdSatellite = &gpsdata->skyview[iGpsd];
561
562       prn = (unsigned int) gpsdSatellite->PRN;
563       elevation = gpsdSatellite->elevation;
564       azimuth = (unsigned int) gpsdSatellite->azimuth;
565       snr = (unsigned int) lrint(gpsdSatellite->ss);
566       inUse = gpsdSatellite->used;
567 #endif
568
569       infoSatellite->prn = prn;
570       infoSatellite->elevation = elevation;
571       infoSatellite->azimuth = azimuth;
572       infoSatellite->snr = snr;
573       info->satellites.inViewCount++;
574
575       if (inUse) {
576         info->satellites.inUse[iGpsd] = prn;
577         info->satellites.inUseCount++;
578       }
579     }
580   }
581   nmeaInfoSetPresent(&info->present, //
582       NMEALIB_PRESENT_SATINUSECOUNT //
583       | NMEALIB_PRESENT_SATINUSE //
584       | NMEALIB_PRESENT_SATINVIEWCOUNT //
585       | NMEALIB_PRESENT_SATINVIEW);
586   gpsdata->set &= ~SATELLITE_SET;
587
588   nmeaInfoSanitise(info);
589
590   if (gpsdata->set) {
591     syslog(LOG_WARNING, "Unhandled bits in gpsdata->set: %llx", (long long unsigned int) gpsdata->set);
592   }
593 }
594
595 void readFromGpsd(GpsDaemon *gpsd, struct gps_data_t *gpsdata, struct GpsdConnectionState *connectionTracking, NmeaInfo *nmeaInfo) {
596   int gpsReadCode;
597
598   if (!gpsd //
599       || !gpsdata //
600       || !connectionTracking //
601       || !nmeaInfo) {
602     return;
603   }
604
605   errno = 0;
606   if (!connectionTracking->connected) {
607     gpsReadCode = -1;
608   } else {
609 #if ((GPSD_API_MAJOR_VERSION >= 7) && (GPSD_API_MINOR_VERSION >= 0))
610     char msg[] = "\0";
611     int msg_len = 1;
612     gpsReadCode = gps_read(gpsdata, msg, msg_len);
613 #else
614     gpsReadCode = gps_read(gpsdata);
615 #endif
616   }
617
618   if (gpsReadCode > 0) {
619     /* data received from gpsd */
620     nmeaInfoFromGpsd(gpsdata, nmeaInfo, connectionTracking);
621   } else if (gpsReadCode < 0) {
622     /* failed to receive data from gpsd */
623
624     if (connectionTracking->connected) {
625       if (errno) {
626         syslog(LOG_WARNING, "Disconnected from gpsd: %s", strerror(errno));
627       } else {
628         syslog(LOG_WARNING, "Disconnected from gpsd");
629       }
630
631       gpsdDisconnect(gpsdata, connectionTracking);
632       nmeaInfoClear(nmeaInfo);
633     }
634
635     connectionTracking->connected = gpsdConnect(gpsd, gpsdata, connectionTracking);
636     nmeaTimeSet(&nmeaInfo->utc, &nmeaInfo->present, NULL);
637   } else {
638     /* no data received from gpsd */
639   }
640 }