"Initial commit to Gerrit"
[profile/ivi/gpsd.git] / net_dgpsip.c
1 /* net_dgpsip.c -- gather and dispatch DGPS data from DGPSIP servers
2  *
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.
5  */
6 #include <stdlib.h>
7 #include "gpsd_config.h"
8 #include <sys/types.h>
9 #ifndef S_SPLINT_S
10 #ifdef HAVE_SYS_SOCKET_H
11 #include <sys/socket.h>
12 #else
13 #define AF_UNSPEC 0
14 #endif /* HAVE_SYS_SOCKET_H */
15 #include <unistd.h>
16 #endif /* S_SPLINT_S */
17 #include <sys/time.h>
18 #include <stdio.h>
19 #include <math.h>
20 #ifndef S_SPLINT_S
21 #ifdef HAVE_NETDB_H
22 #include <netdb.h>
23 #endif /* HAVE_NETDB_H */
24 #endif /* S_SPLINT_S */
25 #include <string.h>
26 #include <errno.h>
27 #include <fcntl.h>
28
29 #include "gpsd.h"
30
31 /*@ -branchstate */
32 int dgpsip_open(struct gps_context_t *context, const char *dgpsserver)
33 /* open a connection to a DGPSIP server */
34 {
35     char hn[256], buf[BUFSIZ];
36     char *colon, *dgpsport = "rtcm-sc104";
37     int opts;
38
39     if ((colon = strchr(dgpsserver, ':')) != NULL) {
40         dgpsport = colon + 1;
41         *colon = '\0';
42     }
43     if (!getservbyname(dgpsport, "tcp"))
44         dgpsport = DEFAULT_RTCM_PORT;
45
46     context->dsock =
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",
50                     dgpsserver);
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,
54                        VERSION);
55         if (write(context->dsock, buf, strlen(buf)) == (ssize_t) strlen(buf))
56             context->netgnss_service = netgnss_dgpsip;
57         else
58             gpsd_report(LOG_ERROR, "hello to DGPS server %s failed\n",
59                         dgpsserver);
60     } else
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);
65
66     if (opts >= 0)
67         (void)fcntl(context->dsock, F_SETFL, opts | O_NONBLOCK);
68     return context->dsock;
69 }
70
71 /*@ +branchstate */
72
73 void dgpsip_report(struct gps_device_t *session)
74 /* may be time to ship a usage report to the DGPSIP server */
75 {
76     /*
77      * 10 is an arbitrary number, the point is to have gotten several good
78      * fixes before reporting usage to our DGPSIP server.
79      */
80     if (session->context->fixcnt > 10 && !session->context->sentdgps) {
81         session->context->sentdgps = true;
82         if (session->context->dsock > -1) {
83             char buf[BUFSIZ];
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);
91             else
92                 gpsd_report(LOG_IO, "write to dgps FAILED\n");
93         }
94     }
95 }
96
97 #define DGPS_THRESHOLD  1600000 /* max. useful dist. from DGPS server (m) */
98 #define SERVER_SAMPLE   12      /* # of servers within threshold to check */
99
100 struct dgps_server_t
101 {
102     double lat, lon;
103     char server[257];
104     double dist;
105 };
106
107 static int srvcmp(const void *s, const void *t)
108 {
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 */
110 }
111
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 */
115 {
116     struct dgps_server_t keep[SERVER_SAMPLE], hold, *sp, *tp;
117     char buf[BUFSIZ];
118     FILE *sfp = fopen(serverlist, "r");
119
120     if (sfp == NULL) {
121         gpsd_report(LOG_ERROR, "no DGPS server list found.\n");
122         context->dsock = -2;    /* don't try this again */
123         return;
124     }
125
126     for (sp = keep; sp < keep + SERVER_SAMPLE; sp++) {
127         sp->dist = DGPS_THRESHOLD;
128         sp->server[0] = '\0';
129     }
130     /*@ -usedef @*/
131     while (fgets(buf, (int)sizeof(buf), sfp)) {
132         char *cp = strchr(buf, '#');
133         if (cp != NULL)
134             *cp = '\0';
135         if (sscanf(buf, "%lf %lf %256s", &hold.lat, &hold.lon, hold.server) ==
136             3) {
137             hold.dist = earth_distance(lat, lon, hold.lat, hold.lon);
138             tp = NULL;
139             /*
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.
144              */
145             for (sp = keep; sp < keep + SERVER_SAMPLE; sp++)
146                 if (hold.dist < sp->dist
147                     && (tp == NULL || hold.dist > tp->dist))
148                     tp = sp;
149             if (tp != NULL)
150                 memcpy(tp, &hold, sizeof(struct dgps_server_t));
151         }
152     }
153     (void)fclose(sfp);
154
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 */
159         return;
160     }
161     /*@ +usedef @*/
162
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)
170                 break;
171         }
172     }
173 }