1 /**
2 * collectd - src/gps.c
3 * Copyright (C) 2015 Nicolas JOURDEN
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Nicolas JOURDEN <nicolas.jourden at laposte.net>
25 **/
29 #include "collectd.h"
30 #include "common.h"
31 #include "plugin.h"
34 #define GPS_DEFAULT_HOST "localhost"
35 #define GPS_DEFAULT_PORT "2947"
36 #define GPS_DEFAULT_TIMEOUT 15
37 #define GPS_DEFAULT_PAUSE 1
40 #if HAVE_GPS_H
41 #include <gps.h>
42 #endif
44 #if HAVE_LIBPTHREAD
45 #include <pthread.h>
46 #endif
49 typedef struct
50 {
51 char *host;
52 char *port;
53 int timeout;
54 int pause;
55 } gps_definition_t;
56 static gps_definition_t gps_data_config;
59 typedef struct {
60 int satellites;
61 double vdop;
62 double hdop;
63 } gpsdata_t;
64 static gpsdata_t gps_data_read;
67 static const char *config_keys[] =
68 {
69 "Host",
70 "Port",
71 "Timeout",
72 "Pause"
73 };
74 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
77 // Thread items:
78 static pthread_t connector = (pthread_t) 0;
79 static pthread_mutex_t data_mutex = PTHREAD_MUTEX_INITIALIZER;
82 /**
83 * Thread reading from gpsd.
84 */
85 static void * gps_collectd_thread (void * pData)
86 {
87 struct gps_data_t gps_data;
89 while (1)
90 {
91 if (gps_open(gps_data_config.host, gps_data_config.port, &gps_data) < 0)
92 {
93 WARNING ("gps: cannot connect to: %s:%s", gps_data_config.host, gps_data_config.port);
94 sleep(60);
95 continue;
96 }
98 gps_stream(&gps_data, WATCH_ENABLE | WATCH_JSON | WATCH_NEWSTYLE, NULL);
99 gps_send(&gps_data, "?WATCH={\"enable\":true,\"json\":true,\"nmea\":false}\r\n");
101 while (1)
102 {
103 if (gps_waiting (&gps_data, gps_data_config.timeout ) )
104 {
105 DEBUG ("gps: reading\n");
107 if (gps_read (&gps_data) == -1)
108 {
109 WARNING ("gps: incorrect data !\n");
110 }
111 else {
112 pthread_mutex_lock (&data_mutex);
113 DEBUG ("gps: parsing\n");
115 // Dop data:
116 if (isnan(gps_data.dop.vdop) == 0)
117 {
118 DEBUG ("gps: isnan(gps_data.dop.vdop) == 0 [OK]\n");
119 gps_data_read.vdop = gps_data.dop.vdop;
120 }
121 if (isnan(gps_data.dop.hdop) == 0)
122 {
123 DEBUG ("gps: isnan(gps_data.dop.hdop) == 0 [OK]\n");
124 gps_data_read.hdop = gps_data.dop.hdop;
125 }
127 // Sat in view:
128 if ((gps_data.set & LATLON_SET))
129 {
130 DEBUG ("gps: gps_data.set & LATLON_SET [OK] ... \n");
131 gps_data_read.satellites = gps_data.satellites_used;
132 }
134 DEBUG ("gps: raw is hdop=%1.3f, vdop=%1.3f, sat-used=%02d, lat=%02.05f, lon=%03.05f\n",
135 gps_data.dop.hdop,
136 gps_data.dop.vdop,
137 gps_data.satellites_used,
138 gps_data.fix.latitude,
139 gps_data.fix.longitude
140 );
142 pthread_mutex_unlock (&data_mutex);
143 sleep(gps_data_config.pause);
144 }
145 }
146 }
147 }
149 gps_stream(&gps_data, WATCH_DISABLE, NULL);
150 gps_close(&gps_data);
152 pthread_exit ((void *)0);
153 }
156 /**
157 * Submit a piece of the data.
158 */
159 static void gps_collectd_submit (const char *type, gauge_t value, const char *type_instance)
160 {
161 value_t values[1];
162 value_list_t vl = VALUE_LIST_INIT;
164 values[0].gauge = value;
166 vl.values = values;
167 vl.values_len = 1;
168 sstrncpy (vl.host, hostname_g, sizeof (vl.host));
169 sstrncpy (vl.plugin, "gps", sizeof (vl.plugin));
170 sstrncpy (vl.type, type, sizeof (vl.type));
171 sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance));
173 plugin_dispatch_values (&vl);
174 }
177 /**
178 * Read the data and submit by piece.
179 */
180 static int gps_collectd_read ()
181 {
182 pthread_mutex_lock (&data_mutex);
183 gps_collectd_submit("dilution_of_precision", (gauge_t) gps_data_read.hdop, "horizontal");
184 gps_collectd_submit("dilution_of_precision", (gauge_t) gps_data_read.vdop, "vertical");
185 gps_collectd_submit("satellites", (gauge_t) gps_data_read.satellites, "gps");
186 DEBUG ("gps: hdop=%1.3f, vdop=%1.3f, sat=%02d.\n",
187 gps_data_read.hdop,
188 gps_data_read.vdop,
189 gps_data_read.satellites
190 );
191 pthread_mutex_unlock (&data_mutex);
192 return (0);
193 }
196 /**
197 * Read configuration.
198 */
199 static int gps_collectd_config (const char *key, const char *value)
200 {
201 char *endptr = NULL;
203 if (strcasecmp (key, "Host") == 0)
204 {
205 if (gps_data_config.host != NULL)
206 {
207 free (gps_data_config.host);
208 }
209 gps_data_config.host = sstrdup (value);
210 }
211 if (strcasecmp (key, "Port") == 0)
212 {
213 if (gps_data_config.port != NULL)
214 {
215 free (gps_data_config.port);
216 }
217 gps_data_config.port = sstrdup (value);
218 }
219 if (strcasecmp (key, "Timeout") == 0)
220 {
221 gps_data_config.timeout = (int) ( strtod(value, &endptr) * 1000 );
222 DEBUG ("gps: will use pause %s - %d.\n", value, gps_data_config.timeout);
223 }
224 if (strcasecmp (key, "Pause") == 0)
225 {
226 gps_data_config.pause = (int) (strtod (value, &endptr));
227 DEBUG ("gps: will use pause %s - %d.\n", value, gps_data_config.pause);
228 }
229 return (0);
230 }
233 /**
234 * Init.
235 */
236 static int gps_collectd_init (void)
237 {
238 int err = 0;
240 DEBUG ("gps: will use %s:%s, timeout %d ms, pause %d sec.\n", gps_data_config.host, gps_data_config.port, gps_data_config.timeout, gps_data_config.pause);
242 err = plugin_thread_create (&connector, NULL, gps_collectd_thread, NULL);
244 if (err != 0)
245 {
246 ERROR ("gps: pthread_create() failed.");
247 return (-1);
248 }
250 return (0);
251 }
254 /**
255 * Shutdown.
256 */
257 static int gps_collectd_shutdown (void)
258 {
259 if (connector != ((pthread_t) 0))
260 {
261 pthread_kill (connector, SIGTERM);
262 connector = (pthread_t) 0;
263 }
265 sfree (gps_data_config.port);
266 sfree (gps_data_config.host);
268 return (0);
269 }
271 /**
272 * Register the module.
273 */
274 void module_register (void)
275 {
276 gps_data_config.host = sstrdup (GPS_DEFAULT_HOST);
277 gps_data_config.port = sstrdup (GPS_DEFAULT_PORT);
278 gps_data_config.timeout = GPS_DEFAULT_TIMEOUT;
279 gps_data_config.pause = GPS_DEFAULT_PAUSE;
280 gps_data_read.hdop = 0;
281 gps_data_read.vdop = 0;
282 gps_data_read.satellites = 0;
284 // Read the config params:
285 plugin_register_config ("gps", gps_collectd_config, config_keys, config_keys_num);
286 // Create the thread:
287 plugin_register_init ("gps", gps_collectd_init);
288 // Kill the thread and stop.
289 plugin_register_shutdown ("gps", gps_collectd_shutdown);
290 // Read plugin:
291 plugin_register_read ("gps", gps_collectd_read);
292 }