gpsdecode.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. /*
  2. * This file is Copyright 2010 by the GPSD project
  3. * SPDX-License-Identifier: BSD-2-clause
  4. */
  5. #include "../include/gpsd_config.h" /* must be before all includes */
  6. #ifdef HAVE_GETOPT_LONG
  7. #include <getopt.h> // for getopt_long()
  8. #endif
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <stdbool.h>
  12. #include <string.h>
  13. #include <stdarg.h>
  14. #include <unistd.h>
  15. #include "../include/gpsd.h" // for gpsd_hexdump()
  16. #include "../include/bits.h"
  17. #include "../include/gps_json.h"
  18. #include "../include/strfuncs.h"
  19. static int verbose = 0;
  20. static bool scaled = true;
  21. static bool json = true;
  22. static bool pseudonmea = false;
  23. static bool split24 = false;
  24. static bool minlength = false;
  25. static unsigned int ntypes = 0;
  26. static unsigned int typelist[32];
  27. static struct gps_context_t context;
  28. /**************************************************************************
  29. *
  30. * Generic machinery
  31. *
  32. **************************************************************************/
  33. #ifdef AIVDM_ENABLE
  34. static const char *raw_hexdump(char *scbuf, size_t scbuflen, int structured,
  35. char *binbuf, size_t binbuflen)
  36. {
  37. if (!structured)
  38. return gpsd_hexdump(scbuf, scbuflen, binbuf, binbuflen);
  39. /* Data parsed as structured doesn't have correct raw data */
  40. #ifndef SQUELCH_ENABLE
  41. size_t len =
  42. (size_t) ((binbuflen >
  43. MAX_PACKET_LENGTH) ? MAX_PACKET_LENGTH : binbuflen) * 2;
  44. if (len > scbuflen - 1) len = scbuflen - 1;
  45. memset(scbuf, 'x', len);
  46. scbuf[len] = '\0';
  47. #else /* SQUELCH defined */
  48. scbuf[0] = '\0';
  49. #endif /* SQUELCH_ENABLE */
  50. return scbuf;
  51. }
  52. static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen)
  53. {
  54. char scratchbuf[MAX_PACKET_LENGTH*2+1];
  55. bool imo = false;
  56. (void)snprintf(buf, buflen, "%u|%u|%09u|", ais->type, ais->repeat,
  57. ais->mmsi);
  58. switch (ais->type) {
  59. case 1: /* Position Report */
  60. case 2:
  61. case 3:
  62. str_appendf(buf, buflen,
  63. "%u|%d|%u|%u|%d|%d|%u|%u|%u|0x%x|%u|0x%x",
  64. ais->type1.status,
  65. ais->type1.turn,
  66. ais->type1.speed,
  67. (unsigned int) ais->type1.accuracy,
  68. ais->type1.lon,
  69. ais->type1.lat,
  70. ais->type1.course,
  71. ais->type1.heading,
  72. ais->type1.second,
  73. ais->type1.maneuver,
  74. (unsigned int) ais->type1.raim, ais->type1.radio);
  75. break;
  76. case 4: /* Base Station Report */
  77. case 11: /* UTC/Date Response */
  78. str_appendf(buf, buflen,
  79. "%04u-%02u-%02uT%02u:%02u:%02uZ|%u|%d|%d|%u|%u|0x%x",
  80. ais->type4.year,
  81. ais->type4.month,
  82. ais->type4.day,
  83. ais->type4.hour,
  84. ais->type4.minute,
  85. ais->type4.second,
  86. (unsigned int) ais->type4.accuracy,
  87. ais->type4.lon,
  88. ais->type4.lat,
  89. ais->type4.epfd,
  90. (unsigned int) ais->type4.raim, ais->type4.radio);
  91. break;
  92. case 5: /* Ship static and voyage related data */
  93. str_appendf(buf, buflen,
  94. "%u|%u|%s|%s|%u|%u|%u|%u|%u|%u|%02u-%02uT%02u:%02uZ"
  95. "|%u|%s|%u",
  96. ais->type5.imo,
  97. ais->type5.ais_version,
  98. ais->type5.callsign,
  99. ais->type5.shipname,
  100. ais->type5.shiptype,
  101. ais->type5.to_bow,
  102. ais->type5.to_stern,
  103. ais->type5.to_port,
  104. ais->type5.to_starboard,
  105. ais->type5.epfd,
  106. ais->type5.month,
  107. ais->type5.day,
  108. ais->type5.hour,
  109. ais->type5.minute,
  110. ais->type5.draught,
  111. ais->type5.destination, ais->type5.dte);
  112. break;
  113. case 6: /* Binary Message */
  114. str_appendf(buf, buflen,
  115. "%u|%u|%u|%u|%u",
  116. ais->type6.seqno,
  117. ais->type6.dest_mmsi,
  118. (unsigned int) ais->type6.retransmit,
  119. ais->type6.dac,
  120. ais->type6.fid);
  121. switch(ais->type6.dac) {
  122. case 235: /* UK */
  123. case 250: /* Rep. Of Ireland */
  124. switch(ais->type6.fid) {
  125. case 10: /* GLA - AtoN monitoring */
  126. str_appendf(buf, buflen,
  127. "|%u|%u|%u|%u|%u|%u|%u|%u",
  128. ais->type6.dac235fid10.ana_int,
  129. ais->type6.dac235fid10.ana_ext1,
  130. ais->type6.dac235fid10.ana_ext2,
  131. ais->type6.dac235fid10.racon,
  132. ais->type6.dac235fid10.light,
  133. (unsigned int)ais->type6.dac235fid10.alarm,
  134. ais->type6.dac235fid10.stat_ext,
  135. (unsigned int)ais->type6.dac235fid10.off_pos);
  136. imo = true;
  137. break;
  138. }
  139. break;
  140. }
  141. if (!imo)
  142. str_appendf(buf, buflen,
  143. "|%zd:%s",
  144. ais->type6.bitcount,
  145. raw_hexdump(scratchbuf, sizeof(scratchbuf),
  146. ais->type6.structured,
  147. ais->type6.bitdata,
  148. BITS_TO_BYTES(ais->type6.bitcount)));
  149. break;
  150. case 7: /* Binary Acknowledge */
  151. case 13: /* Safety Related Acknowledge */
  152. str_appendf(buf, buflen,
  153. "%u|%u|%u|%u",
  154. ais->type7.mmsi1,
  155. ais->type7.mmsi2, ais->type7.mmsi3, ais->type7.mmsi4);
  156. break;
  157. case 8: /* Binary Broadcast Message */
  158. str_appendf(buf, buflen, "%u|%u", ais->type8.dac, ais->type8.fid);
  159. switch(ais->type8.dac) {
  160. case 1: /* International */
  161. switch(ais->type8.fid) {
  162. case 11: /* IMO236 - Met/Hydro message */
  163. str_appendf(buf, buflen,
  164. "|%d|%d|%02uT%02u:%02uZ|%u|%u|%u|%u|%u|%u|%u"
  165. "|%u|%u|%u|%d|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u"
  166. "|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u",
  167. ais->type8.dac1fid11.lon,
  168. ais->type8.dac1fid11.lat,
  169. ais->type8.dac1fid11.day,
  170. ais->type8.dac1fid11.hour,
  171. ais->type8.dac1fid11.minute,
  172. ais->type8.dac1fid11.wspeed,
  173. ais->type8.dac1fid11.wgust,
  174. ais->type8.dac1fid11.wdir,
  175. ais->type8.dac1fid11.wgustdir,
  176. ais->type8.dac1fid11.airtemp,
  177. ais->type8.dac1fid11.humidity,
  178. ais->type8.dac1fid11.dewpoint,
  179. ais->type8.dac1fid11.pressure,
  180. ais->type8.dac1fid11.pressuretend,
  181. ais->type8.dac1fid11.visibility,
  182. ais->type8.dac1fid11.waterlevel,
  183. ais->type8.dac1fid11.leveltrend,
  184. ais->type8.dac1fid11.cspeed,
  185. ais->type8.dac1fid11.cdir,
  186. ais->type8.dac1fid11.cspeed2,
  187. ais->type8.dac1fid11.cdir2,
  188. ais->type8.dac1fid11.cdepth2,
  189. ais->type8.dac1fid11.cspeed3,
  190. ais->type8.dac1fid11.cdir3,
  191. ais->type8.dac1fid11.cdepth3,
  192. ais->type8.dac1fid11.waveheight,
  193. ais->type8.dac1fid11.waveperiod,
  194. ais->type8.dac1fid11.wavedir,
  195. ais->type8.dac1fid11.swellheight,
  196. ais->type8.dac1fid11.swellperiod,
  197. ais->type8.dac1fid11.swelldir,
  198. ais->type8.dac1fid11.seastate,
  199. ais->type8.dac1fid11.watertemp,
  200. ais->type8.dac1fid11.preciptype,
  201. ais->type8.dac1fid11.salinity,
  202. ais->type8.dac1fid11.ice);
  203. imo = true;
  204. break;
  205. case 31: /* IMO289 - Met/Hydro message */
  206. str_appendf(buf, buflen,
  207. "|%d|%d|%02uT%02u:%02uZ|%u|%u|%u|%u|%d|%u|%d|%u"
  208. "|%u|%u|%d|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u"
  209. "|%u|%u|%u|%u|%d|%u|%u|%u",
  210. ais->type8.dac1fid31.lon,
  211. ais->type8.dac1fid31.lat,
  212. ais->type8.dac1fid31.day,
  213. ais->type8.dac1fid31.hour,
  214. ais->type8.dac1fid31.minute,
  215. ais->type8.dac1fid31.wspeed,
  216. ais->type8.dac1fid31.wgust,
  217. ais->type8.dac1fid31.wdir,
  218. ais->type8.dac1fid31.wgustdir,
  219. ais->type8.dac1fid31.airtemp,
  220. ais->type8.dac1fid31.humidity,
  221. ais->type8.dac1fid31.dewpoint,
  222. ais->type8.dac1fid31.pressure,
  223. ais->type8.dac1fid31.pressuretend,
  224. ais->type8.dac1fid31.visibility,
  225. ais->type8.dac1fid31.waterlevel,
  226. ais->type8.dac1fid31.leveltrend,
  227. ais->type8.dac1fid31.cspeed,
  228. ais->type8.dac1fid31.cdir,
  229. ais->type8.dac1fid31.cspeed2,
  230. ais->type8.dac1fid31.cdir2,
  231. ais->type8.dac1fid31.cdepth2,
  232. ais->type8.dac1fid31.cspeed3,
  233. ais->type8.dac1fid31.cdir3,
  234. ais->type8.dac1fid31.cdepth3,
  235. ais->type8.dac1fid31.waveheight,
  236. ais->type8.dac1fid31.waveperiod,
  237. ais->type8.dac1fid31.wavedir,
  238. ais->type8.dac1fid31.swellheight,
  239. ais->type8.dac1fid31.swellperiod,
  240. ais->type8.dac1fid31.swelldir,
  241. ais->type8.dac1fid31.seastate,
  242. ais->type8.dac1fid31.watertemp,
  243. ais->type8.dac1fid31.preciptype,
  244. ais->type8.dac1fid31.salinity,
  245. ais->type8.dac1fid31.ice);
  246. imo = true;
  247. break;
  248. }
  249. break;
  250. }
  251. if (!imo)
  252. str_appendf(buf, buflen,
  253. "|%zd:%s",
  254. ais->type8.bitcount,
  255. raw_hexdump(scratchbuf, sizeof(scratchbuf),
  256. ais->type8.structured,
  257. ais->type8.bitdata,
  258. BITS_TO_BYTES(ais->type8.bitcount)));
  259. break;
  260. case 9:
  261. str_appendf(buf, buflen,
  262. "%u|%u|%u|%d|%d|%u|%u|0x%x|%u|%u|0x%x",
  263. ais->type9.alt,
  264. ais->type9.speed,
  265. (unsigned int) ais->type9.accuracy,
  266. ais->type9.lon,
  267. ais->type9.lat,
  268. ais->type9.course,
  269. ais->type9.second,
  270. ais->type9.regional,
  271. ais->type9.dte,
  272. (unsigned int) ais->type9.raim, ais->type9.radio);
  273. break;
  274. case 10: /* UTC/Date Inquiry */
  275. str_appendf(buf, buflen, "%u", ais->type10.dest_mmsi);
  276. break;
  277. case 12: /* Safety Related Message */
  278. str_appendf(buf, buflen,
  279. "%u|%u|%u|%s",
  280. ais->type12.seqno,
  281. ais->type12.dest_mmsi,
  282. (unsigned int) ais->type12.retransmit, ais->type12.text);
  283. break;
  284. case 14: /* Safety Related Broadcast Message */
  285. str_appendf(buf, buflen, "%s", ais->type14.text);
  286. break;
  287. case 15:
  288. str_appendf(buf, buflen,
  289. "%u|%u|%u|%u|%u|%u|%u|%u",
  290. ais->type15.mmsi1,
  291. ais->type15.type1_1,
  292. ais->type15.offset1_1,
  293. ais->type15.type1_2,
  294. ais->type15.offset1_2,
  295. ais->type15.mmsi2,
  296. ais->type15.type2_1, ais->type15.offset2_1);
  297. break;
  298. case 16:
  299. str_appendf(buf, buflen,
  300. "%u|%u|%u|%u|%u|%u",
  301. ais->type16.mmsi1,
  302. ais->type16.offset1,
  303. ais->type16.increment1,
  304. ais->type16.mmsi2,
  305. ais->type16.offset2, ais->type16.increment2);
  306. break;
  307. case 17:
  308. str_appendf(buf, buflen,
  309. "%d|%d|%zd:%s",
  310. ais->type17.lon,
  311. ais->type17.lat,
  312. ais->type17.bitcount,
  313. gpsd_hexdump(scratchbuf, sizeof(scratchbuf),
  314. ais->type17.bitdata,
  315. BITS_TO_BYTES(ais->type17.bitcount)));
  316. break;
  317. case 18:
  318. str_appendf(buf, buflen,
  319. "%u|%u|%u|%d|%d|%u|%u|%u|0x%x|%u|%u|%u|%u|%u|%u|0x%x",
  320. ais->type18.reserved,
  321. ais->type18.speed,
  322. (unsigned int) ais->type18.accuracy,
  323. ais->type18.lon,
  324. ais->type18.lat,
  325. ais->type18.course,
  326. ais->type18.heading,
  327. ais->type18.second,
  328. ais->type18.regional,
  329. (unsigned int) ais->type18.cs,
  330. (unsigned int) ais->type18.display,
  331. (unsigned int) ais->type18.dsc,
  332. (unsigned int) ais->type18.band,
  333. (unsigned int) ais->type18.msg22,
  334. (unsigned int) ais->type18.raim, ais->type18.radio);
  335. break;
  336. case 19:
  337. str_appendf(buf, buflen,
  338. "%u|%u|%u|%d|%d|%u|%u|%u|0x%x|%s|%u|%u|%u|%u|%u|%u|%u|%u|%u",
  339. ais->type19.reserved,
  340. ais->type19.speed,
  341. (unsigned int) ais->type19.accuracy,
  342. ais->type19.lon,
  343. ais->type19.lat,
  344. ais->type19.course,
  345. ais->type19.heading,
  346. ais->type19.second,
  347. ais->type19.regional,
  348. ais->type19.shipname,
  349. ais->type19.shiptype,
  350. ais->type19.to_bow,
  351. ais->type19.to_stern,
  352. ais->type19.to_port,
  353. ais->type19.to_starboard,
  354. ais->type19.epfd,
  355. (unsigned int) ais->type19.raim,
  356. ais->type19.dte, (unsigned int) ais->type19.assigned);
  357. break;
  358. case 20: /* Data Link Management Message */
  359. str_appendf(buf, buflen,
  360. "%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u|%u",
  361. ais->type20.offset1,
  362. ais->type20.number1,
  363. ais->type20.timeout1,
  364. ais->type20.increment1,
  365. ais->type20.offset2,
  366. ais->type20.number2,
  367. ais->type20.timeout2,
  368. ais->type20.increment2,
  369. ais->type20.offset3,
  370. ais->type20.number3,
  371. ais->type20.timeout3,
  372. ais->type20.increment3,
  373. ais->type20.offset4,
  374. ais->type20.number4,
  375. ais->type20.timeout4, ais->type20.increment4);
  376. break;
  377. case 21: /* Aid to Navigation */
  378. str_appendf(buf, buflen,
  379. "%u|%s|%u|%d|%d|%u|%u|%u|%u|%u|%u|%u|0x%x|%u|%u",
  380. ais->type21.aid_type,
  381. ais->type21.name,
  382. (unsigned int) ais->type21.accuracy,
  383. ais->type21.lon,
  384. ais->type21.lat,
  385. ais->type21.to_bow,
  386. ais->type21.to_stern,
  387. ais->type21.to_port,
  388. ais->type21.to_starboard,
  389. ais->type21.epfd,
  390. ais->type21.second,
  391. ais->type21.regional,
  392. (unsigned int) ais->type21.off_position,
  393. (unsigned int) ais->type21.raim,
  394. (unsigned int) ais->type21.virtual_aid);
  395. break;
  396. case 22: /* Channel Management */
  397. if (!ais->type22.addressed)
  398. str_appendf(buf, buflen,
  399. "%u|%u|%u|%u|%d|%d|%d|%d|%u|%u|%u|%u",
  400. ais->type22.channel_a,
  401. ais->type22.channel_b,
  402. ais->type22.txrx,
  403. (unsigned int) ais->type22.power,
  404. ais->type22.area.ne_lon,
  405. ais->type22.area.ne_lat,
  406. ais->type22.area.sw_lon,
  407. ais->type22.area.sw_lat,
  408. (unsigned int)ais->type22.addressed,
  409. (unsigned int)ais->type22.band_a,
  410. (unsigned int)ais->type22.band_b,
  411. ais->type22.zonesize);
  412. else
  413. str_appendf(buf, buflen,
  414. "%u|%u|%u|%u|%u|%u|%u|%u|%u|%u",
  415. ais->type22.channel_a,
  416. ais->type22.channel_b,
  417. ais->type22.txrx,
  418. (unsigned int) ais->type22.power,
  419. ais->type22.mmsi.dest1,
  420. ais->type22.mmsi.dest2,
  421. (unsigned int) ais->type22.addressed,
  422. (unsigned int) ais->type22.band_a,
  423. (unsigned int) ais->type22.band_b,
  424. ais->type22.zonesize);
  425. break;
  426. case 23: /* Group Management Command */
  427. str_appendf(buf, buflen,
  428. "%d|%d|%d|%d|%u|%u|%u|%u|%u",
  429. ais->type23.ne_lon,
  430. ais->type23.ne_lat,
  431. ais->type23.sw_lon,
  432. ais->type23.sw_lat,
  433. ais->type23.stationtype,
  434. ais->type23.shiptype,
  435. ais->type23.txrx,
  436. ais->type23.interval, ais->type23.quiet);
  437. break;
  438. case 24: /* Class B CS Static Data Report */
  439. str_appendf(buf, buflen, "%s|", ais->type24.shipname);
  440. str_appendf(buf, buflen, "%u|", ais->type24.shiptype);
  441. str_appendf(buf, buflen, "%s|", ais->type24.vendorid);
  442. str_appendf(buf, buflen, "%u|", ais->type24.model);
  443. str_appendf(buf, buflen, "%u|", ais->type24.serial);
  444. str_appendf(buf, buflen, "%s|", ais->type24.callsign);
  445. if (AIS_AUXILIARY_MMSI(ais->mmsi)) {
  446. str_appendf(buf, buflen, "%u", ais->type24.mothership_mmsi);
  447. } else {
  448. str_appendf(buf, buflen,
  449. "%u|%u|%u|%u",
  450. ais->type24.dim.to_bow,
  451. ais->type24.dim.to_stern,
  452. ais->type24.dim.to_port,
  453. ais->type24.dim.to_starboard);
  454. }
  455. break;
  456. case 25: /* Binary Message, Single Slot */
  457. str_appendf(buf, buflen,
  458. "%u|%u|%u|%u|%zd:%s",
  459. (unsigned int) ais->type25.addressed,
  460. (unsigned int) ais->type25.structured,
  461. ais->type25.dest_mmsi,
  462. ais->type25.app_id,
  463. ais->type25.bitcount,
  464. gpsd_hexdump(scratchbuf, sizeof(scratchbuf),
  465. ais->type25.bitdata,
  466. BITS_TO_BYTES(ais->type25.bitcount)));
  467. break;
  468. case 26: /* Binary Message, Multiple Slot */
  469. str_appendf(buf, buflen,
  470. "%u|%u|%u|%u|%zd:%s:%u",
  471. (unsigned int) ais->type26.addressed,
  472. (unsigned int) ais->type26.structured,
  473. ais->type26.dest_mmsi,
  474. ais->type26.app_id,
  475. ais->type26.bitcount,
  476. gpsd_hexdump(scratchbuf, sizeof(scratchbuf),
  477. ais->type26.bitdata,
  478. BITS_TO_BYTES(ais->type26.bitcount)),
  479. ais->type26.radio);
  480. break;
  481. case 27: /* Long Range AIS Broadcast message */
  482. str_appendf(buf, buflen,
  483. "%u|%u|%d|%d|%u|%u|%u|%u",
  484. ais->type27.status,
  485. (unsigned int)ais->type27.accuracy,
  486. ais->type27.lon,
  487. ais->type27.lat,
  488. ais->type27.speed,
  489. ais->type27.course,
  490. (unsigned int)ais->type27.raim,
  491. (unsigned int)ais->type27.gnss);
  492. break;
  493. default:
  494. str_appendf(buf, buflen, "unknown AIVDM message content.");
  495. break;
  496. }
  497. (void)strlcat(buf, "\r\n", buflen);
  498. }
  499. #endif
  500. /* say whether a given message should be visible */
  501. static bool filter(gps_mask_t changed, struct gps_device_t *session)
  502. {
  503. if (ntypes == 0)
  504. return true;
  505. else {
  506. unsigned int i, t;
  507. if ((changed & AIS_SET)!=0)
  508. t = session->gpsdata.ais.type;
  509. else if ((changed & RTCM2_SET)!=0)
  510. t = session->gpsdata.rtcm2.type;
  511. else if ((changed & RTCM3_SET)!=0)
  512. t = session->gpsdata.rtcm3.type;
  513. else
  514. return true;
  515. for (i = 0; i < ntypes; i++)
  516. if (t == typelist[i])
  517. return true;
  518. }
  519. return false;
  520. }
  521. /* report pseudo-NMEA in appropriate circumstances */
  522. static void pseudonmea_report(gps_mask_t changed, struct gps_device_t *device)
  523. {
  524. if (GPS_PACKET_TYPE(device->lexer.type)
  525. && !TEXTUAL_PACKET_TYPE(device->lexer.type)) {
  526. char buf[MAX_PACKET_LENGTH * 3 + 2];
  527. if ((changed & REPORT_IS) != 0) {
  528. nmea_tpv_dump(device, buf, sizeof(buf));
  529. (void)fputs(buf, stdout);
  530. }
  531. if ((changed & SATELLITE_SET) != 0) {
  532. nmea_sky_dump(device, buf, sizeof(buf));
  533. (void)fputs(buf, stdout);
  534. }
  535. if ((changed & SUBFRAME_SET) != 0) {
  536. nmea_subframe_dump(device, buf, sizeof(buf));
  537. (void)fputs(buf, stdout);
  538. }
  539. #ifdef AIVDM_ENABLE
  540. if ((changed & AIS_SET) != 0) {
  541. nmea_ais_dump(device, buf, sizeof(buf));
  542. (void)fputs(buf, stdout);
  543. }
  544. #endif /* AIVDM_ENABLE */
  545. }
  546. }
  547. /* sensor data on fpin to dump format on fpout */
  548. static void decode(FILE *fpin, FILE*fpout)
  549. {
  550. struct gps_device_t session;
  551. struct gps_policy_t policy;
  552. size_t minima[PACKET_TYPES+1];
  553. #if defined(SOCKET_EXPORT_ENABLE) || defined(AIVDM_ENABLE)
  554. char buf[GPS_JSON_RESPONSE_MAX * 4];
  555. #endif
  556. int i;
  557. //This looks like a good idea, but it breaks regression tests
  558. //(void)strlcpy(session.gpsdata.dev.path, "stdin",
  559. // sizeof(session.gpsdata.dev.path));
  560. memset(&policy, '\0', sizeof(policy));
  561. policy.json = json;
  562. policy.scaled = scaled;
  563. policy.nmea = pseudonmea;
  564. gpsd_time_init(&context, time(NULL));
  565. context.readonly = true;
  566. gpsd_init(&session, &context, NULL);
  567. gpsd_clear(&session);
  568. session.gpsdata.gps_fd = fileno(fpin);
  569. session.gpsdata.dev.baudrate = 38400; /* hack to enable subframes */
  570. (void)strlcpy(session.gpsdata.dev.path,
  571. "stdin",
  572. sizeof(session.gpsdata.dev.path));
  573. for (i = 0; i < (int)(sizeof(minima)/sizeof(minima[0])); i++)
  574. minima[i] = MAX_PACKET_LENGTH+1;
  575. for (;;)
  576. {
  577. gps_mask_t changed = gpsd_poll(&session);
  578. if (changed == ERROR_SET || changed == NODATA_IS)
  579. break;
  580. if (session.lexer.type == COMMENT_PACKET)
  581. gpsd_set_century(&session);
  582. if (verbose >= 1 && TEXTUAL_PACKET_TYPE(session.lexer.type))
  583. (void)fputs((char *)session.lexer.outbuffer, fpout);
  584. if (session.lexer.outbuflen < minima[session.lexer.type+1])
  585. minima[session.lexer.type+1] = session.lexer.outbuflen;
  586. /* mask should match what's in report_data() */
  587. if ((changed & (REPORT_IS|GST_SET|SATELLITE_SET|SUBFRAME_SET|
  588. ATTITUDE_SET|RTCM2_SET|RTCM3_SET|AIS_SET|
  589. PASSTHROUGH_IS)) == 0)
  590. continue;
  591. if (!filter(changed, &session))
  592. continue;
  593. else if (json) {
  594. if ((changed & PASSTHROUGH_IS) != 0) {
  595. (void)fputs((char *)session.lexer.outbuffer, fpout);
  596. (void)fputs("\n", fpout);
  597. }
  598. #ifdef SOCKET_EXPORT_ENABLE
  599. else {
  600. if ((changed & AIS_SET)!=0) {
  601. if (session.gpsdata.ais.type == 24 &&
  602. session.gpsdata.ais.type24.part != both && !split24)
  603. continue;
  604. }
  605. json_data_report(changed,
  606. &session, &policy,
  607. buf, sizeof(buf));
  608. (void)fputs(buf, fpout);
  609. }
  610. #endif /* SOCKET_EXPORT_ENABLE */
  611. #ifdef AIVDM_ENABLE
  612. } else if (session.lexer.type == AIVDM_PACKET) {
  613. if ((changed & AIS_SET)!=0) {
  614. if (session.gpsdata.ais.type == 24 &&
  615. session.gpsdata.ais.type24.part != both && !split24)
  616. continue;
  617. aivdm_csv_dump(&session.gpsdata.ais, buf, sizeof(buf));
  618. (void)fputs(buf, fpout);
  619. }
  620. #endif /* AIVDM_ENABLE */
  621. }
  622. if (policy.nmea)
  623. pseudonmea_report(changed, &session);
  624. }
  625. if (minlength)
  626. {
  627. for (i = 0; i < (int)(sizeof(minima)/sizeof(minima[0])); i++) {
  628. /* dump all minima, ignoring comments */
  629. if (i != 1 && minima[i] < MAX_PACKET_LENGTH+1) {
  630. const struct gps_type_t **dp;
  631. char *np = "Unknown";
  632. for (dp = gpsd_drivers; *dp; dp++) {
  633. if ((*dp)->packet_type == i-1) {
  634. np = (*dp)->type_name;
  635. break;
  636. }
  637. }
  638. printf("%s (%d): %u\n", np, i-1, (unsigned int)minima[i]);
  639. }
  640. }
  641. }
  642. }
  643. #ifdef SOCKET_EXPORT_ENABLE
  644. /* JSON format on fpin to JSON on fpout - idempotency test */
  645. static void encode(FILE *fpin, FILE *fpout)
  646. {
  647. char inbuf[BUFSIZ];
  648. struct gps_policy_t policy;
  649. struct gps_device_t session;
  650. int lineno = 0;
  651. memset(&policy, '\0', sizeof(policy));
  652. memset(&session, '\0', sizeof(session));
  653. session.context = &context;
  654. context.errout.debug = LOG_SHOUT;
  655. context.errout.label = "gpsdecode";
  656. (void)strlcpy(session.gpsdata.dev.path,
  657. "stdin",
  658. sizeof(session.gpsdata.dev.path));
  659. policy.json = true;
  660. policy.nmea = pseudonmea;
  661. /* Parsing is always made in unscaled mode,
  662. * this policy applies to the dumping */
  663. policy.scaled = scaled;
  664. while (fgets(inbuf, (int)sizeof(inbuf), fpin) != NULL) {
  665. int status;
  666. ++lineno;
  667. if (inbuf[0] == '#')
  668. continue;
  669. status = libgps_json_unpack(inbuf, &session.gpsdata, NULL);
  670. if (status != 0) {
  671. (void)fprintf(stderr,
  672. "gpsdecode: dying with status %d (%s) on line %d\n",
  673. status, json_error_string(status), lineno);
  674. exit(EXIT_FAILURE);
  675. }
  676. json_data_report(session.gpsdata.set,
  677. &session, &policy,
  678. inbuf, sizeof(inbuf));
  679. (void)fputs(inbuf, fpout);
  680. }
  681. }
  682. #endif /* SOCKET_EXPORT_ENABLE */
  683. /* usage()
  684. * print usages, and exit
  685. */
  686. static void usage(void)
  687. {
  688. (void)fprintf(stderr,
  689. "Usage: gpsdecode [OPTIONS]\n"
  690. "\n"
  691. #ifdef HAVE_GETOPT_LONG
  692. " --ais AIS dump format with an ASCII pipe separator.\n"
  693. " --debug DEBUG Set debug level.\n"
  694. " --decode Decode\n"
  695. " --encode Encode\n"
  696. " --help Show this help, then exit\n"
  697. " --json JSON.\n"
  698. " --minlength Minimum length, no JSON.\n"
  699. " --nmea psuedo NMEA\n"
  700. " --split24 split24\n"
  701. " --types TYPES Types\n"
  702. " --unscaled Unscaled\n"
  703. " --verbose Verbose.\n"
  704. " --version Print version, then exit\n"
  705. #endif
  706. " -? Show this help, then exit\n"
  707. " -c AIS dump format with an ASCII pipe separator.\n"
  708. " -D DEBUG Set debug level.\n"
  709. " -d Decode \n"
  710. " -e Encode\n"
  711. " -h Show this help, then exit\n"
  712. " -j JSON.\n"
  713. " -m Minimum length, no JSON\n"
  714. " -n psuedo NMEA\n"
  715. " -s split24 \n"
  716. " -t TYPES Types, comma separated.\n"
  717. " -u Unscaled\n"
  718. " -V Print version and exit.\n"
  719. " -v Verbose.\n"
  720. "\n");
  721. exit(EXIT_FAILURE);
  722. }
  723. int main(int argc, char **argv)
  724. {
  725. int ch;
  726. const char *optstring = "cdehjmnst:uvVD:";
  727. enum { doencode, dodecode } mode = dodecode;
  728. #ifdef HAVE_GETOPT_LONG
  729. int option_index = 0;
  730. static struct option long_options[] = {
  731. {"debug", required_argument, NULL, 'D'},
  732. {"decode", no_argument, NULL, 'd'},
  733. {"encode", no_argument, NULL, 'e'},
  734. {"help", no_argument, NULL, 'h'},
  735. {"json", no_argument, NULL, 'j'},
  736. {"minlength", no_argument, NULL, 'm'},
  737. {"nmea", no_argument, NULL, 'n'},
  738. {"nojson", no_argument, NULL, 'c'},
  739. {"split24", no_argument, NULL, 's'},
  740. {"types", required_argument, NULL, 't'},
  741. {"unscaled", no_argument, NULL, 'u' },
  742. {"verbose", no_argument, NULL, 'v' },
  743. {"version", no_argument, NULL, 'V' },
  744. {NULL, 0, NULL, 0},
  745. };
  746. #endif
  747. gps_context_init(&context, "gpsdecode");
  748. while (1) {
  749. #ifdef HAVE_GETOPT_LONG
  750. ch = getopt_long(argc, argv, optstring, long_options, &option_index);
  751. #else
  752. ch = getopt(argc, argv, optstring);
  753. #endif
  754. if (ch == -1) {
  755. break;
  756. }
  757. switch (ch) {
  758. case 'c':
  759. json = false;
  760. break;
  761. case 'd':
  762. mode = dodecode;
  763. break;
  764. case 'D':
  765. context.errout.debug = verbose = atoi(optarg);
  766. json_enable_debug(verbose - 2, stderr);
  767. break;
  768. case 'e':
  769. mode = doencode;
  770. break;
  771. case 'j':
  772. json = true;
  773. break;
  774. case 'm':
  775. minlength = true;
  776. json = false;
  777. break;
  778. case 'n':
  779. pseudonmea = true;
  780. break;
  781. case 's':
  782. split24 = true;
  783. break;
  784. case 't':
  785. typelist[ntypes++] = (unsigned int)atoi(strtok(optarg, ","));
  786. for(;;) {
  787. char *next = strtok(NULL, ",");
  788. if (next == NULL)
  789. break;
  790. typelist[ntypes++] = (unsigned int)atoi(next);
  791. }
  792. break;
  793. case 'u':
  794. scaled = false;
  795. break;
  796. case 'v':
  797. verbose = 1;
  798. break;
  799. case 'V':
  800. (void)fprintf(stderr, "gpsdecode revision " VERSION "\n");
  801. exit(EXIT_SUCCESS);
  802. case '?':
  803. case 'h':
  804. default:
  805. usage();
  806. }
  807. }
  808. //argc -= optind;
  809. //argv += optind;
  810. if (mode == doencode) {
  811. #ifdef SOCKET_EXPORT_ENABLE
  812. encode(stdin, stdout);
  813. #else
  814. (void)fprintf(stderr, "gpsdecode: encoding support isn't compiled.\n");
  815. exit(EXIT_FAILURE);
  816. #endif /* SOCKET_EXPORT_ENABLE */
  817. } else
  818. decode(stdin, stdout);
  819. exit(EXIT_SUCCESS);
  820. }
  821. /* gpsdecode.c ends here */
  822. // vim: set expandtab shiftwidth=4