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