1 /* net_dgpsip.c -- gather and dispatch DGPS data from DGPSIP servers
3 * This file is Copyright (c) 2010 by the GPSD project
4 * BSD terms apply: see the file COPYING in the distribution root for details.
7 #include "gpsd_config.h"
10 #ifdef HAVE_SYS_SOCKET_H
11 #include <sys/socket.h>
14 #endif /* HAVE_SYS_SOCKET_H */
16 #endif /* S_SPLINT_S */
23 #endif /* HAVE_NETDB_H */
24 #endif /* S_SPLINT_S */
32 int dgpsip_open(struct gps_context_t *context, const char *dgpsserver)
33 /* open a connection to a DGPSIP server */
35 char hn[256], buf[BUFSIZ];
36 char *colon, *dgpsport = "rtcm-sc104";
39 if ((colon = strchr(dgpsserver, ':')) != NULL) {
43 if (!getservbyname(dgpsport, "tcp"))
44 dgpsport = DEFAULT_RTCM_PORT;
47 netlib_connectsock(AF_UNSPEC, dgpsserver, dgpsport, "tcp");
48 if (context->dsock >= 0) {
49 gpsd_report(LOG_PROG, "connection to DGPS server %s established.\n",
51 (void)gethostname(hn, sizeof(hn));
52 /* greeting required by some RTCM104 servers; others will ignore it */
53 (void)snprintf(buf, sizeof(buf), "HELO %s gpsd %s\r\nR\r\n", hn,
55 if (write(context->dsock, buf, strlen(buf)) == (ssize_t) strlen(buf))
56 context->netgnss_service = netgnss_dgpsip;
58 gpsd_report(LOG_ERROR, "hello to DGPS server %s failed\n",
61 gpsd_report(LOG_ERROR,
62 "can't connect to DGPS server %s, netlib error %d.\n",
63 dgpsserver, context->dsock);
64 opts = fcntl(context->dsock, F_GETFL);
67 (void)fcntl(context->dsock, F_SETFL, opts | O_NONBLOCK);
68 return context->dsock;
73 void dgpsip_report(struct gps_device_t *session)
74 /* may be time to ship a usage report to the DGPSIP server */
77 * 10 is an arbitrary number, the point is to have gotten several good
78 * fixes before reporting usage to our DGPSIP server.
80 if (session->context->fixcnt > 10 && !session->context->sentdgps) {
81 session->context->sentdgps = true;
82 if (session->context->dsock > -1) {
84 (void)snprintf(buf, sizeof(buf), "R %0.8f %0.8f %0.2f\r\n",
85 session->gpsdata.fix.latitude,
86 session->gpsdata.fix.longitude,
87 session->gpsdata.fix.altitude);
88 if (write(session->context->dsock, buf, strlen(buf)) ==
89 (ssize_t) strlen(buf))
90 gpsd_report(LOG_IO, "=> dgps %s\n", buf);
92 gpsd_report(LOG_IO, "write to dgps FAILED\n");
97 #define DGPS_THRESHOLD 1600000 /* max. useful dist. from DGPS server (m) */
98 #define SERVER_SAMPLE 12 /* # of servers within threshold to check */
107 static int srvcmp(const void *s, const void *t)
109 return (int)(((const struct dgps_server_t *)s)->dist - ((const struct dgps_server_t *)t)->dist); /* fixes: warning: cast discards qualifiers from pointer target type */
112 void dgpsip_autoconnect(struct gps_context_t *context,
113 double lat, double lon, const char *serverlist)
114 /* tell the library to talk to the nearest DGPSIP server */
116 struct dgps_server_t keep[SERVER_SAMPLE], hold, *sp, *tp;
118 FILE *sfp = fopen(serverlist, "r");
121 gpsd_report(LOG_ERROR, "no DGPS server list found.\n");
122 context->dsock = -2; /* don't try this again */
126 for (sp = keep; sp < keep + SERVER_SAMPLE; sp++) {
127 sp->dist = DGPS_THRESHOLD;
128 sp->server[0] = '\0';
131 while (fgets(buf, (int)sizeof(buf), sfp)) {
132 char *cp = strchr(buf, '#');
135 if (sscanf(buf, "%lf %lf %256s", &hold.lat, &hold.lon, hold.server) ==
137 hold.dist = earth_distance(lat, lon, hold.lat, hold.lon);
140 * The idea here is to look for a server in the sample array
141 * that is (a) closer than the one we're checking, and (b)
142 * furtherest away of all those that are closer. Replace it.
143 * In this way we end up with the closest possible set.
145 for (sp = keep; sp < keep + SERVER_SAMPLE; sp++)
146 if (hold.dist < sp->dist
147 && (tp == NULL || hold.dist > tp->dist))
150 memcpy(tp, &hold, sizeof(struct dgps_server_t));
155 if (keep[0].server[0] == '\0') {
156 gpsd_report(LOG_ERROR, "no DGPS servers within %dm.\n",
157 (int)(DGPS_THRESHOLD / 1000));
158 context->dsock = -2; /* don't try this again */
163 /* sort them and try the closest first */
164 qsort((void *)keep, SERVER_SAMPLE, sizeof(struct dgps_server_t), srvcmp);
165 for (sp = keep; sp < keep + SERVER_SAMPLE; sp++) {
166 if (sp->server[0] != '\0') {
167 gpsd_report(LOG_INF, "%s is %dkm away.\n", sp->server,
168 (int)(sp->dist / 1000));
169 if (dgpsip_open(context, sp->server) >= 0)