gpsdecode.c 33 KB

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