gps2udp.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /*
  2. * gps2udp
  3. *
  4. * Dump NMEA to UDP socket for AIShub
  5. * gps2udp -u data.aishub.net:1234
  6. *
  7. * Author: Fulup Ar Foll (directly inspired from gpspipe.c)
  8. * Date: 2013-03-01
  9. *
  10. * This file is Copyright 2013 by the GPSD project
  11. * SPDX-License-Identifier: BSD-2-clause
  12. *
  13. */
  14. #include "../include/gpsd_config.h" /* must be before all includes */
  15. #include <arpa/inet.h>
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <fcntl.h>
  19. #ifdef HAVE_GETOPT_LONG
  20. #include <getopt.h> // for getopt_long()
  21. #endif
  22. #include <netdb.h> /* for gethostbyname() */
  23. #include <netinet/in.h>
  24. #include <stdbool.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. // do not use strsep() it is not POSIX
  28. #include <string.h> /* for strlcpy(), strtok(), etc. */
  29. #include <strings.h>
  30. #include <sys/select.h>
  31. #include <sys/socket.h>
  32. #include <sys/stat.h>
  33. #include <sys/time.h>
  34. #include <sys/types.h>
  35. #include <termios.h>
  36. #include <time.h>
  37. #include <unistd.h>
  38. #include "../include/gpsd.h"
  39. #include "../include/gpsdclient.h"
  40. #include "../include/strfuncs.h"
  41. #include "../include/timespec.h"
  42. #define MAX_TIME_LEN 80
  43. #define MAX_GPSD_RETRY 10
  44. static struct gps_data_t gpsdata;
  45. /* UDP socket variables */
  46. #define MAX_UDP_DEST 5
  47. static struct sockaddr_in remote[MAX_UDP_DEST];
  48. static int sock[MAX_UDP_DEST];
  49. static int udpchannel;
  50. /* gpsclient source */
  51. static struct fixsource_t gpsd_source;
  52. static unsigned int flags;
  53. static int debug = 0;
  54. static bool aisonly = false;
  55. static char* time2string(void)
  56. /* return local time hh:mm:ss */
  57. {
  58. static char buffer[MAX_TIME_LEN];
  59. time_t curtime;
  60. struct tm *loctime;
  61. /* Get the current time. */
  62. curtime = time (NULL);
  63. /* Convert it to local time representation. */
  64. loctime = localtime (&curtime);
  65. /* Print it out in a nice format. */
  66. (void)strftime (buffer, sizeof(buffer), "%H:%M:%S", loctime);
  67. return (buffer);
  68. }
  69. static int send_udp (char *nmeastring, size_t ind)
  70. {
  71. char message[MAX_PACKET_LENGTH];
  72. char *buffer;
  73. int channel;
  74. /* if string length is unknown make a copy and compute it */
  75. if (ind == 0) {
  76. /* compute message size and add 0x0a 0x0d */
  77. for (ind=0; nmeastring [ind] != '\0'; ind ++) {
  78. if (ind >= sizeof(message) - 3) {
  79. (void)fprintf(stderr, "gps2udp: too big [%s] \n", nmeastring);
  80. return -1;
  81. }
  82. message[ind] = nmeastring[ind];
  83. }
  84. buffer = message;
  85. } else {
  86. /* use directly nmeastring but change terminition */
  87. buffer = nmeastring;
  88. ind = ind-1;
  89. }
  90. /* Add termination to NMEA feed for AISHUB */
  91. buffer[ind] = '\r'; ind++;
  92. buffer[ind] = '\n'; ind++;
  93. buffer[ind] = '\0';
  94. if ((flags & WATCH_JSON)==0 && buffer[0] == '{') {
  95. /* do not send JSON when not configured to do so */
  96. return 0;
  97. }
  98. /* send message on udp channel */
  99. for (channel=0; channel < udpchannel; channel ++) {
  100. ssize_t status = sendto(sock[channel],
  101. buffer,
  102. ind,
  103. 0,
  104. (struct sockaddr *)&remote[channel],
  105. (int)sizeof(remote));
  106. if (status < (ssize_t)ind) {
  107. (void)fprintf(stderr, "gps2udp: failed to send [%s] \n",
  108. nmeastring);
  109. return -1;
  110. }
  111. }
  112. return 0;
  113. }
  114. /* Open and bind udp socket to host */
  115. static int open_udp(char **hostport)
  116. {
  117. int channel;
  118. for (channel = 0; channel < udpchannel; channel++) {
  119. char *hostname = NULL;
  120. char *portname = NULL;
  121. char *endptr = NULL;
  122. int portnum;
  123. struct hostent *hp;
  124. if (NULL == hostport[channel]) {
  125. // pacify coverity
  126. (void)fprintf(stderr, "gps2udp: syntax is [-u hostname:port]\n");
  127. return (-1);
  128. }
  129. /* parse argument */
  130. hostname = strtok(hostport[channel], ":");
  131. // NULL tells strtok() to resume search from last found token
  132. portname = strtok(NULL, ":");
  133. if ((hostname == NULL) || (portname == NULL)) {
  134. (void)fprintf(stderr, "gps2udp: syntax is [-u hostname:port]\n");
  135. return (-1);
  136. }
  137. errno = 0;
  138. portnum = (int)strtol(portname, &endptr, 10);
  139. if (1 > portnum || 65535 < portnum || '\0' != *endptr || 0 != errno) {
  140. (void)fprintf(stderr, "gps2udp: syntax is [-u hostname:port] "
  141. "[%s] is not a valid port number\n", portname);
  142. return (-1);
  143. }
  144. sock[channel]= socket(AF_INET, SOCK_DGRAM, 0);
  145. if (sock[channel] < 0) {
  146. (void)fprintf(stderr, "gps2udp: error creating UDP socket\n");
  147. return (-1);
  148. }
  149. remote[channel].sin_family = (sa_family_t)AF_INET;
  150. hp = gethostbyname(hostname);
  151. if (hp==NULL) {
  152. (void)fprintf(stderr,
  153. "gps2udp: syntax is [-u hostname:port] [%s]"
  154. " is not a valid hostname\n",
  155. hostname);
  156. return (-1);
  157. }
  158. memcpy( &remote[channel].sin_addr, hp->h_addr_list[0], hp->h_length);
  159. remote[channel].sin_port = htons((in_port_t)portnum);
  160. }
  161. return (0);
  162. }
  163. static void usage(void)
  164. {
  165. (void)fprintf(stderr,
  166. "Usage: gps2udp [OPTIONS] [server[:port[:device]]]\n\n"
  167. #ifdef HAVE_GETOPT_LONG
  168. " --ais Select AIS messages only.\n"
  169. " --count COUNT exit after count packets.\n"
  170. " --daemonize Daemonize\n"
  171. " --debug DEBUGLEVEL\n"
  172. " --help Show this help, then exit\n"
  173. " --json Feed JSON messages only.\n"
  174. " --nmea Feed NMEA messages only.\n"
  175. " --version Show version, then exit\n"
  176. #endif
  177. " -a Select AIS messages only.\n"
  178. " -b Run in background as a daemon.\n"
  179. " -c COUNT Exit after count packets.\n"
  180. " -d [0-2] 1 display sent packets, "
  181. "2 display ignored packets.\n"
  182. " -h Show this help.\n"
  183. " -j Feed JSON.\n"
  184. " -n Feed NMEA.\n"
  185. " -u HOST:PORT Send UDP NMEA/JSON feed to host:port "
  186. "multiple -u accepted]\n"
  187. " -V Print version and exit.\n"
  188. "\n"
  189. "example: gps2udp -a -n -c 2 -d 1 -u data.aishub.net:2222 "
  190. "fridu.net\n"
  191. );
  192. }
  193. static void connect2gpsd(bool restart)
  194. /* loop until we connect with gpsd */
  195. {
  196. unsigned int delay;
  197. if (restart) {
  198. (void)gps_close(&gpsdata);
  199. if (debug > 0)
  200. (void)fprintf(stdout,
  201. "gps2udp [%s] reset gpsd connection\n",
  202. time2string());
  203. }
  204. /* loop until we reach GPSd */
  205. for (delay = 10; ; delay = delay * 2) {
  206. int status = gps_open(gpsd_source.server, gpsd_source.port, &gpsdata);
  207. if (status != 0) {
  208. (void)fprintf(stderr,
  209. "gps2udp [%s] connection failed at %s:%s\n",
  210. time2string(), gpsd_source.server, gpsd_source.port);
  211. (void)sleep(delay);
  212. } else {
  213. if (debug > 0)
  214. (void)fprintf(stdout, "gps2udp [%s] connect to gpsd %s:%s\n",
  215. time2string(), gpsd_source.server,
  216. gpsd_source.port);
  217. break;
  218. }
  219. }
  220. /* select the right set of gps data */
  221. (void)gps_stream(&gpsdata, flags, gpsd_source.device);
  222. }
  223. /* get data from gpsd */
  224. static ssize_t read_gpsd(char *message, size_t len)
  225. {
  226. int ind;
  227. char c;
  228. int retry=0;
  229. struct timespec to;
  230. /* allow room for trailing NUL */
  231. len--;
  232. /* loop until we get some data or an error */
  233. for (ind = 0; ind < (int)len;) {
  234. /* prepare for a blocking read with a 10s timeout */
  235. to.tv_sec = 10;
  236. to.tv_nsec = 0;
  237. int result = nanowait(gpsdata.gps_fd, &to);
  238. switch (result)
  239. {
  240. case 1: /* we have data waiting, let's process them */
  241. result = (int)read(gpsdata.gps_fd, &c, 1);
  242. /* If we lost gpsd connection reset it */
  243. if (result != 1) {
  244. connect2gpsd (true);
  245. }
  246. if ((c == '\n') || (c == '\r')) {
  247. message[ind]='\0';
  248. if (ind > 0) {
  249. if (retry > 0) {
  250. if (debug ==1)
  251. (void)fprintf (stdout,"\r");
  252. if (debug > 1)
  253. (void)fprintf(stdout,
  254. " [%s] No Data for: %ds\n",
  255. time2string(), retry*10);
  256. }
  257. if (aisonly && message[0] != '!') {
  258. if (debug >1)
  259. (void)fprintf(stdout,
  260. ".... [%s %d] %s\n", time2string(),
  261. ind, message);
  262. return(0);
  263. }
  264. }
  265. return ((ssize_t)ind+1);
  266. } else {
  267. message[ind]= c;
  268. ind++;
  269. }
  270. break;
  271. case 0: /* no data fail in timeout */
  272. retry++;
  273. /* if too many empty packets are received reset gpsd connection */
  274. if (retry > MAX_GPSD_RETRY) {
  275. connect2gpsd(true);
  276. retry = 0;
  277. }
  278. if (debug > 0)
  279. ignore_return(write (1, ".", 1));
  280. break;
  281. default: /* we lost connection with gpsd */
  282. connect2gpsd(true);
  283. break;
  284. }
  285. }
  286. message[ind] = '\0';
  287. (void)fprintf (stderr,"\n gps2udp: message too big [%s]\n", message);
  288. return(-1);
  289. }
  290. static unsigned char AISto6bit(unsigned char c)
  291. /* 6 bits decoding of AIS payload */
  292. {
  293. unsigned char cp = c;
  294. if(c < (unsigned char)0x30)
  295. return (unsigned char)-1;
  296. if(c > (unsigned char)0x77)
  297. return (unsigned char)-1;
  298. if(((unsigned char)0x57 < c) && (c < (unsigned char)0x60))
  299. return (unsigned char)-1;
  300. cp += (unsigned char)0x28;
  301. if(cp > (unsigned char)0x80)
  302. cp += (unsigned char)0x20;
  303. else
  304. cp += (unsigned char)0x28;
  305. return (unsigned char)(cp & (unsigned char)0x3f);
  306. }
  307. /* get MMSI from AIS bit string */
  308. static unsigned int AISGetInt(unsigned char *bitbytes, unsigned int sp,
  309. unsigned int len)
  310. {
  311. unsigned int acc = 0;
  312. unsigned int s0p = sp-1; // to zero base
  313. unsigned int i;
  314. for(i = 0; i < len; i++) {
  315. unsigned int cp, cx, c0;
  316. acc = acc << 1;
  317. cp = (s0p + i) / 6;
  318. cx = (unsigned int)bitbytes[cp]; // what if cp >= byte_length?
  319. c0 = (cx >> (5 - ((s0p + i) % 6))) & 1;
  320. acc |= c0;
  321. }
  322. return acc;
  323. }
  324. int main(int argc, char **argv)
  325. {
  326. bool daemonize = false;
  327. long count = -1;
  328. int ch;
  329. char *udphostport[MAX_UDP_DEST];
  330. const char *optstring = "?habnjVc:l:u:d:";
  331. #ifdef HAVE_GETOPT_LONG
  332. int option_index = 0;
  333. static struct option long_options[] = {
  334. {"ais", no_argument, NULL, 'h'},
  335. {"count", required_argument, NULL, 'c'},
  336. {"daemonize", no_argument, NULL, 'b'},
  337. {"debug", required_argument, NULL, 'd'},
  338. {"help", no_argument, NULL, 'h'},
  339. {"json", no_argument, NULL, 'j'},
  340. {"nmea", no_argument, NULL, 'n'},
  341. {"version", no_argument, NULL, 'V' },
  342. {NULL, 0, NULL, 0},
  343. };
  344. #endif
  345. // pacify covarity
  346. memset(udphostport, 0, sizeof(udphostport));
  347. flags = WATCH_ENABLE;
  348. while (1) {
  349. #ifdef HAVE_GETOPT_LONG
  350. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  351. #else
  352. ch = getopt(argc, argv, optstring);
  353. #endif
  354. if (ch == -1) {
  355. break;
  356. }
  357. switch (ch) {
  358. case 'a':
  359. aisonly = true;
  360. break;
  361. case 'b':
  362. daemonize = true;
  363. break;
  364. case 'c':
  365. count = atol(optarg);
  366. break;
  367. case 'd':
  368. debug = atoi(optarg);
  369. if ((debug <1) || (debug > 2)) {
  370. usage();
  371. exit(1);
  372. }
  373. break;
  374. case 'j':
  375. if (debug >0)
  376. (void)fprintf(stdout, "JSON selected\n");
  377. flags |= WATCH_JSON;
  378. break;
  379. case 'n':
  380. if (debug >0)
  381. (void)fprintf(stdout, "NMEA selected\n");
  382. flags |= WATCH_NMEA;
  383. break;
  384. case 'u':
  385. if (udpchannel >= MAX_UDP_DEST) {
  386. (void)fprintf(stderr,
  387. "gps2udp: too many UDP destinations (max=%d)\n",
  388. MAX_UDP_DEST);
  389. } else {
  390. udphostport[udpchannel++] = optarg;
  391. }
  392. break;
  393. case '?':
  394. case 'h':
  395. default:
  396. usage();
  397. exit(1);
  398. case 'V':
  399. (void)fprintf(stderr, "%s: %s (revision %s)\n",
  400. argv[0], VERSION, REVISION);
  401. exit(0);
  402. }
  403. }
  404. /* Grok the server, port, and device. */
  405. if (optind < argc)
  406. gpsd_source_spec(argv[optind], &gpsd_source);
  407. else
  408. gpsd_source_spec(NULL, &gpsd_source);
  409. if (gpsd_source.device != NULL)
  410. flags |= WATCH_DEVICE;
  411. /* check before going background if we can connect to gpsd */
  412. connect2gpsd(false);
  413. /* Open UDP port */
  414. if (udpchannel > 0) {
  415. int status = open_udp(udphostport);
  416. if (status != 0) exit (1);
  417. }
  418. /* Daemonize if the user requested it. */
  419. if (daemonize) {
  420. if (os_daemon(0, 0) != 0) {
  421. (void)fprintf(stderr,
  422. "gps2udp: daemonization failed: %s\n",
  423. strerror(errno));
  424. }
  425. }
  426. /* infinite loop to get data from gpsd and push them to aggregators */
  427. for (;;) {
  428. char buffer[MAX_PACKET_LENGTH];
  429. ssize_t len;
  430. len = read_gpsd(buffer, sizeof(buffer));
  431. /* ignore empty message */
  432. if (len > 3) {
  433. if (debug > 0) {
  434. (void)fprintf (stdout,"---> [%s] -- %s",time2string(),buffer);
  435. // Try to extract MMSI from AIS payload
  436. if (str_starts_with(buffer, "!AIVDM")) {
  437. #define MAX_INFO 6
  438. int i, j;
  439. char packet[MAX_PACKET_LENGTH];
  440. char *adrpkt = packet;
  441. unsigned char *info[MAX_INFO];
  442. unsigned int mmsi;
  443. unsigned char bitstrings[255];
  444. int info_5_len;
  445. // pacify coverity.
  446. memset(bitstrings, 0, sizeof(bitstrings));
  447. // strtok break original string
  448. (void)strlcpy(packet, buffer, sizeof(packet));
  449. for (j = 0; j < MAX_INFO; j++) {
  450. info[j] = (unsigned char *)strtok(adrpkt, ",");
  451. // have strtok() continue from last position
  452. adrpkt = NULL;
  453. }
  454. info_5_len = (int)strlen((char *)info[5]);
  455. if (info_5_len >= ((int)sizeof(bitstrings) - 1)) {
  456. info_5_len = (int)sizeof(bitstrings) - 1;
  457. }
  458. for(i = 0 ; i < info_5_len; i++) {
  459. bitstrings[i] = AISto6bit(info[5][i]);
  460. }
  461. mmsi = AISGetInt(bitstrings, 9, 30);
  462. (void)fprintf(stdout," MMSI=%9u", mmsi);
  463. }
  464. (void)fprintf(stdout,"\n");
  465. }
  466. // send to all UDP destinations
  467. if (udpchannel > 0)
  468. (void)send_udp(buffer, (size_t)len);
  469. // if we count messages check it now
  470. if (count >= 0) {
  471. if (count-- == 0) {
  472. /* completed count */
  473. (void)fprintf(stderr,
  474. "gpsd2udp: normal exit after counted "
  475. "packets\n");
  476. exit (0);
  477. }
  478. } // end count
  479. } // end len > 3
  480. } // end for (;;)
  481. // This is an infinite loop, should never be here
  482. (void)fprintf (stderr, "gpsd2udp ERROR abnormal exit\n");
  483. exit (-1);
  484. }
  485. /* end */
  486. // vim: set expandtab shiftwidth=4