1 /****************************************************************************
4 gpsd_json.c - move data between in-core and JSON structures
7 These are functions (used only by the daemon) to dump the contents
8 of various core data structures in JSON.
11 Written by Eric S. Raymond, 2009
12 This file is Copyright (c) 2010 by the GPSD project
13 BSD terms apply: see the file COPYING in the distribution root for details.
15 ***************************************************************************/
29 * Manifest names for the gnss_type enum - must be kept synced with it.
30 * Also, masks so we can tell what packet types correspond to each class.
32 /* the map of device class names */
38 #define CLASSMAP_NITEMS 5
40 struct classmap_t classmap[CLASSMAP_NITEMS] = {
41 /* name typemask packetmask */
43 {"GPS", SEEN_GPS, GPS_TYPEMASK},
44 {"RTCM2", SEEN_RTCM2, PACKET_TYPEMASK(RTCM2_PACKET)},
45 {"RTCM3", SEEN_RTCM3, PACKET_TYPEMASK(RTCM3_PACKET)},
46 {"AIS", SEEN_AIS, PACKET_TYPEMASK(AIVDM_PACKET)},
50 char *json_stringify( /*@out@*/ char *to,
52 /*@in@*/ const char *from)
53 /* escape double quotes and control characters inside a JSON string */
61 * The limit is len-6 here because we need to be leave room for
62 * each character to generate an up to 6-character Java-style
65 for (sp = from; *sp != '\0' && ((tp - to) < ((int)len - 5)); sp++) {
85 /* ugh, we'd prefer a C-style escape here, but this is JSON */
86 (void)snprintf(tp, 5, "%u04x", (unsigned int)*sp);
90 if (*sp == '"' || *sp == '\\')
101 void json_version_dump( /*@out@*/ char *reply, size_t replylen)
103 (void)snprintf(reply, replylen,
104 "{\"class\":\"VERSION\",\"release\":\"%s\",\"rev\":\"%s\",\"proto_major\":%d,\"proto_minor\":%d}\r\n",
106 GPSD_PROTO_MAJOR_VERSION, GPSD_PROTO_MINOR_VERSION);
109 void json_tpv_dump(const struct gps_data_t *gpsdata,
110 /*@out@*/ char *reply, size_t replylen)
112 assert(replylen > 2);
113 (void)strlcpy(reply, "{\"class\":\"TPV\",", replylen);
114 (void)snprintf(reply + strlen(reply),
115 replylen - strlen(reply),
117 gpsdata->tag[0] != '\0' ? gpsdata->tag : "-");
118 (void)snprintf(reply + strlen(reply),
119 replylen - strlen(reply),
120 "\"device\":\"%s\",", gpsdata->dev.path);
121 if (isnan(gpsdata->fix.time) == 0)
122 (void)snprintf(reply + strlen(reply),
123 replylen - strlen(reply),
124 "\"time\":%.3f,", gpsdata->fix.time);
125 if (isnan(gpsdata->fix.ept) == 0)
126 (void)snprintf(reply + strlen(reply),
127 replylen - strlen(reply),
128 "\"ept\":%.3f,", gpsdata->fix.ept);
130 * Suppressing TPV fields that would be invalid because the fix
131 * quality doesn't support them is nice for cutting down on the
132 * volume of meaningless output, but the real reason to do it is
133 * that we've observed that geodetic fix computation is unstable
134 * in a way that tends to change low-order digits in invalid
135 * fixes. Dumping these tends to cause cross-architecture failures
136 * in the regression tests. Rgus effect has been seen on SiRF-II
137 * chips, which are quite common.
139 if (gpsdata->fix.mode >= MODE_2D) {
140 if (isnan(gpsdata->fix.latitude) == 0)
141 (void)snprintf(reply + strlen(reply),
142 replylen - strlen(reply),
143 "\"lat\":%.9f,", gpsdata->fix.latitude);
144 if (isnan(gpsdata->fix.longitude) == 0)
145 (void)snprintf(reply + strlen(reply),
146 replylen - strlen(reply),
147 "\"lon\":%.9f,", gpsdata->fix.longitude);
148 if (gpsdata->fix.mode >= MODE_3D && isnan(gpsdata->fix.altitude) == 0)
149 (void)snprintf(reply + strlen(reply),
150 replylen - strlen(reply),
151 "\"alt\":%.3f,", gpsdata->fix.altitude);
152 if (isnan(gpsdata->fix.epx) == 0)
153 (void)snprintf(reply + strlen(reply),
154 replylen - strlen(reply),
155 "\"epx\":%.3f,", gpsdata->fix.epx);
156 if (isnan(gpsdata->fix.epy) == 0)
157 (void)snprintf(reply + strlen(reply),
158 replylen - strlen(reply),
159 "\"epy\":%.3f,", gpsdata->fix.epy);
160 if ((gpsdata->fix.mode >= MODE_3D) && isnan(gpsdata->fix.epv) == 0)
161 (void)snprintf(reply + strlen(reply),
162 replylen - strlen(reply),
163 "\"epv\":%.3f,", gpsdata->fix.epv);
164 if (isnan(gpsdata->fix.track) == 0)
165 (void)snprintf(reply + strlen(reply),
166 replylen - strlen(reply),
167 "\"track\":%.4f,", gpsdata->fix.track);
168 if (isnan(gpsdata->fix.speed) == 0)
169 (void)snprintf(reply + strlen(reply),
170 replylen - strlen(reply),
171 "\"speed\":%.3f,", gpsdata->fix.speed);
172 if ((gpsdata->fix.mode >= MODE_3D) && isnan(gpsdata->fix.climb) == 0)
173 (void)snprintf(reply + strlen(reply),
174 replylen - strlen(reply),
175 "\"climb\":%.3f,", gpsdata->fix.climb);
176 if (isnan(gpsdata->fix.epd) == 0)
177 (void)snprintf(reply + strlen(reply),
178 replylen - strlen(reply),
179 "\"epd\":%.4f,", gpsdata->fix.epd);
180 if (isnan(gpsdata->fix.eps) == 0)
181 (void)snprintf(reply + strlen(reply),
182 replylen - strlen(reply),
183 "\"eps\":%.2f,", gpsdata->fix.eps);
184 if ((gpsdata->fix.mode >= MODE_3D) && isnan(gpsdata->fix.epc) == 0)
185 (void)snprintf(reply + strlen(reply),
186 replylen - strlen(reply),
187 "\"epc\":%.2f,", gpsdata->fix.epc);
189 (void)snprintf(reply + strlen(reply),
190 replylen - strlen(reply),
191 "\"mode\":%d,", gpsdata->fix.mode);
192 if (reply[strlen(reply) - 1] == ',')
193 reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
194 (void)strlcat(reply, "}\r\n", sizeof(reply) - strlen(reply));
197 void json_sky_dump(const struct gps_data_t *datap,
198 /*@out@*/ char *reply, size_t replylen)
200 int i, j, used, reported = 0;
201 assert(replylen > 2);
202 (void)strlcpy(reply, "{\"class\":\"SKY\",", replylen);
203 (void)snprintf(reply + strlen(reply),
204 replylen - strlen(reply),
206 datap->tag[0] != '\0' ? datap->tag : "-");
207 (void)snprintf(reply + strlen(reply),
208 replylen - strlen(reply),
209 "\"device\":\"%s\",", datap->dev.path);
210 if (isnan(datap->skyview_time) == 0)
211 (void)snprintf(reply + strlen(reply),
212 replylen - strlen(reply),
213 "\"time\":%.3f,", datap->skyview_time);
214 if (isnan(datap->dop.xdop) == 0)
215 (void)snprintf(reply + strlen(reply),
216 replylen - strlen(reply),
217 "\"xdop\":%.2f,", datap->dop.xdop);
218 if (isnan(datap->dop.ydop) == 0)
219 (void)snprintf(reply + strlen(reply),
220 replylen - strlen(reply),
221 "\"ydop\":%.2f,", datap->dop.ydop);
222 if (isnan(datap->dop.vdop) == 0)
223 (void)snprintf(reply + strlen(reply),
224 replylen - strlen(reply),
225 "\"vdop\":%.2f,", datap->dop.vdop);
226 if (isnan(datap->dop.tdop) == 0)
227 (void)snprintf(reply + strlen(reply),
228 replylen - strlen(reply),
229 "\"tdop\":%.2f,", datap->dop.tdop);
230 if (isnan(datap->dop.hdop) == 0)
231 (void)snprintf(reply + strlen(reply),
232 replylen - strlen(reply),
233 "\"hdop\":%.2f,", datap->dop.hdop);
234 if (isnan(datap->dop.gdop) == 0)
235 (void)snprintf(reply + strlen(reply),
236 replylen - strlen(reply),
237 "\"gdop\":%.2f,", datap->dop.gdop);
238 if (isnan(datap->dop.pdop) == 0)
239 (void)snprintf(reply + strlen(reply),
240 replylen - strlen(reply),
241 "\"pdop\":%.2f,", datap->dop.pdop);
242 /* insurance against flaky drivers */
243 for (i = 0; i < datap->satellites_visible; i++)
247 (void)strlcat(reply, "\"satellites\":[", replylen);
248 for (i = 0; i < reported; i++) {
250 for (j = 0; j < datap->satellites_used; j++)
251 if (datap->used[j] == datap->PRN[i]) {
256 (void)snprintf(reply + strlen(reply),
257 replylen - strlen(reply),
258 "{\"PRN\":%d,\"el\":%d,\"az\":%d,\"ss\":%.0f,\"used\":%s},",
260 datap->elevation[i], datap->azimuth[i],
261 datap->ss[i], used ? "true" : "false");
266 if (reply[strlen(reply) - 1] == ',')
267 reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
268 (void)strlcat(reply, "]", replylen - strlen(reply));
270 if (reply[strlen(reply) - 1] == ',')
271 reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
272 (void)strlcat(reply, "}\r\n", replylen - strlen(reply));
273 if (datap->satellites_visible != reported)
274 gpsd_report(LOG_WARN, "Satellite count %d != PRN count %d\n",
275 datap->satellites_visible, reported);
278 void json_device_dump(const struct gps_device_t *device,
279 /*@out@*/ char *reply, size_t replylen)
281 char buf1[JSON_VAL_MAX * 2 + 1];
282 struct classmap_t *cmp;
283 (void)strlcpy(reply, "{\"class\":\"DEVICE\",\"path\":\"", replylen);
284 (void)strlcat(reply, device->gpsdata.dev.path, replylen);
285 (void)strlcat(reply, "\",", replylen);
286 if (device->gpsdata.online > 0) {
287 (void)snprintf(reply + strlen(reply), replylen - strlen(reply),
288 "\"activated\":%2.2f,", device->gpsdata.online);
289 if (device->observed != 0) {
291 for (cmp = classmap; cmp < classmap + NITEMS(classmap); cmp++)
292 if ((device->observed & cmp->packetmask) != 0)
293 mask |= cmp->typemask;
295 (void)snprintf(reply + strlen(reply),
296 replylen - strlen(reply), "\"flags\":%d,",
299 if (device->device_type != NULL) {
300 (void)strlcat(reply, "\"driver\":\"", replylen);
301 (void)strlcat(reply, device->device_type->type_name, replylen);
302 (void)strlcat(reply, "\",", replylen);
305 if (device->subtype[0] != '\0') {
306 (void)strlcat(reply, "\"subtype\":\"", replylen);
308 json_stringify(buf1, sizeof(buf1), device->subtype),
310 (void)strlcat(reply, "\",", replylen);
313 if (device->is_serial) {
314 (void)snprintf(reply + strlen(reply), replylen - strlen(reply),
315 "\"native\":%d,\"bps\":%d,\"parity\":\"%c\",\"stopbits\":%u,\"cycle\":%2.2f",
316 device->gpsdata.dev.driver_mode,
317 (int)gpsd_get_speed(&device->ttyset),
318 device->gpsdata.dev.parity,
319 device->gpsdata.dev.stopbits,
320 device->gpsdata.dev.cycle);
321 #ifdef ALLOW_RECONFIGURE
322 if (device->device_type != NULL
323 && device->device_type->rate_switcher != NULL)
324 (void)snprintf(reply + strlen(reply),
325 replylen - strlen(reply),
326 ",\"mincycle\":%2.2f",
327 device->device_type->min_cycle);
328 #endif /* ALLOW_RECONFIGURE */
331 if (reply[strlen(reply) - 1] == ',')
332 reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
333 (void)strlcat(reply, "}\r\n", replylen);
336 void json_watch_dump(const struct policy_t *ccp,
337 /*@out@*/ char *reply, size_t replylen)
340 (void)snprintf(reply, replylen,
341 "{\"class\":\"WATCH\",\"enable\":%s,\"json\":%s,\"nmea\":%s,\"raw\":%d,\"scaled\":%s,\"timing\":%s",
342 ccp->watcher ? "true" : "false",
343 ccp->json ? "true" : "false",
344 ccp->nmea ? "true" : "false",
346 ccp->scaled ? "true" : "false",
347 ccp->timing ? "true" : "false");
348 if (ccp->devpath[0] != '\0')
349 (void)snprintf(reply + strlen(reply), replylen - strlen(reply),
350 "\"device\":%s,", ccp->devpath);
351 if (reply[strlen(reply) - 1] == ',')
352 reply[strlen(reply) - 1] = '\0';
353 (void)strlcat(reply, "}\r\n", replylen - strlen(reply));
357 #if defined(RTCM104V2_ENABLE)
358 void rtcm2_json_dump(const struct rtcm2_t *rtcm, /*@out@*/ char buf[],
360 /* dump the contents of a parsed RTCM104 message as JSON */
363 char buf1[JSON_VAL_MAX * 2 + 1];
365 * Beware! Needs to stay synchronized with a JSON enumeration map in
366 * the parser. This interpretation of NAVSYSTEM_GALILEO is assumed
367 * from RTCM3, it's not actually documented in RTCM 2.1.
369 static char *navsysnames[] = { "GPS", "GLONASS", "GALILEO" };
373 (void)snprintf(buf, buflen,
374 "{\"class\":\"RTCM2\",\"type\":%u,\"station_id\":%u,\"zcount\":%0.1f,\"seqnum\":%u,\"length\":%u,\"station_health\":%u,",
375 rtcm->type, rtcm->refstaid, rtcm->zcount, rtcm->seqnum,
376 rtcm->length, rtcm->stathlth);
378 switch (rtcm->type) {
381 (void)strlcat(buf, "\"satellites\":[", buflen);
382 for (n = 0; n < rtcm->ranges.nentries; n++) {
383 const struct rangesat_t *rsp = &rtcm->ranges.sat[n];
384 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
385 "{\"ident\":%u,\"udre\":%u,\"issuedata\":%u,\"rangerr\":%0.3f,\"rangerate\":%0.3f},",
388 rsp->issuedata, rsp->rangerr, rsp->rangerate);
390 if (buf[strlen(buf) - 1] == ',')
391 buf[strlen(buf) - 1] = '\0';
392 (void)strlcat(buf, "]", buflen);
396 if (rtcm->ecef.valid)
397 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
398 "\"x\":%.2f,\"y\":%.2f,\"z\":%.2f,",
399 rtcm->ecef.x, rtcm->ecef.y, rtcm->ecef.z);
403 if (rtcm->reference.valid) {
404 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
405 "\"system\":\"%s\",\"sense\":%1d,\"datum\":\"%s\",\"dx\":%.1f,\"dy\":%.1f,\"dz\":%.1f,",
406 rtcm->reference.system >= NITEMS(navsysnames)
408 : navsysnames[rtcm->reference.system],
409 rtcm->reference.sense,
410 rtcm->reference.datum,
412 rtcm->reference.dy, rtcm->reference.dz);
417 #define JSON_BOOL(x) ((x)?"true":"false")
418 (void)strlcat(buf, "\"satellites\":[", buflen);
419 for (n = 0; n < rtcm->conhealth.nentries; n++) {
420 const struct consat_t *csp = &rtcm->conhealth.sat[n];
421 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
422 "{\"ident\":%u,\"iodl\":%s,\"health\":%1u,\"snr\":%d,\"health_en\":%s,\"new_data\":%s,\"los_warning\":%s,\"tou\":%u},",
424 JSON_BOOL(csp->iodl),
425 (unsigned)csp->health,
427 JSON_BOOL(csp->health_en),
428 JSON_BOOL(csp->new_data),
429 JSON_BOOL(csp->los_warning), csp->tou);
432 if (buf[strlen(buf) - 1] == ',')
433 buf[strlen(buf) - 1] = '\0';
434 (void)strlcat(buf, "]", buflen);
437 case 6: /* NOP msg */
441 (void)strlcat(buf, "\"satellites\":[", buflen);
442 for (n = 0; n < rtcm->almanac.nentries; n++) {
443 const struct station_t *ssp = &rtcm->almanac.station[n];
444 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
445 "{\"lat\":%.4f,\"lon\":%.4f,\"range\":%u,\"frequency\":%.1f,\"health\":%u,\"station_id\":%u,\"bitrate\":%u},",
450 ssp->health, ssp->station_id, ssp->bitrate);
452 if (buf[strlen(buf) - 1] == ',')
453 buf[strlen(buf) - 1] = '\0';
454 (void)strlcat(buf, "]", buflen);
457 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
458 "\"message\":\"%s\"", json_stringify(buf1,
464 (void)strlcat(buf, "\"data\":[", buflen);
465 for (n = 0; n < rtcm->length; n++)
466 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
467 "\"0x%08x\",", rtcm->words[n]);
468 if (buf[strlen(buf) - 1] == ',')
469 buf[strlen(buf) - 1] = '\0';
470 (void)strlcat(buf, "]", buflen);
474 if (buf[strlen(buf) - 1] == ',')
475 buf[strlen(buf) - 1] = '\0';
476 (void)strlcat(buf, "}\r\n", buflen);
479 #endif /* defined(RTCM104V2_ENABLE) */
481 #if defined(AIVDM_ENABLE)
483 void aivdm_json_dump(const struct ais_t *ais, bool scaled,
484 /*@out@*/ char *buf, size_t buflen)
486 char buf1[JSON_VAL_MAX * 2 + 1];
487 char buf2[JSON_VAL_MAX * 2 + 1];
488 char buf3[JSON_VAL_MAX * 2 + 1];
490 static char *nav_legends[] = {
491 "Under way using engine",
494 "Restricted manoeuverability",
495 "Constrained by her draught",
498 "Engaged in fishing",
508 static char *epfd_legends[] = {
512 "Combined GPS/GLONASS",
515 "Integrated navigation system",
520 static char *ship_type_legends[100] = {
522 "Reserved for future use",
523 "Reserved for future use",
524 "Reserved for future use",
525 "Reserved for future use",
526 "Reserved for future use",
527 "Reserved for future use",
528 "Reserved for future use",
529 "Reserved for future use",
530 "Reserved for future use",
531 "Reserved for future use",
532 "Reserved for future use",
533 "Reserved for future use",
534 "Reserved for future use",
535 "Reserved for future use",
536 "Reserved for future use",
537 "Reserved for future use",
538 "Reserved for future use",
539 "Reserved for future use",
540 "Reserved for future use",
541 "Wing in ground (WIG) - all ships of this type",
542 "Wing in ground (WIG) - Hazardous category A",
543 "Wing in ground (WIG) - Hazardous category B",
544 "Wing in ground (WIG) - Hazardous category C",
545 "Wing in ground (WIG) - Hazardous category D",
546 "Wing in ground (WIG) - Reserved for future use",
547 "Wing in ground (WIG) - Reserved for future use",
548 "Wing in ground (WIG) - Reserved for future use",
549 "Wing in ground (WIG) - Reserved for future use",
550 "Wing in ground (WIG) - Reserved for future use",
553 "Towing: length exceeds 200m or breadth exceeds 25m",
554 "Dredging or underwater ops",
561 "High speed craft (HSC) - all ships of this type",
562 "High speed craft (HSC) - Hazardous category A",
563 "High speed craft (HSC) - Hazardous category B",
564 "High speed craft (HSC) - Hazardous category C",
565 "High speed craft (HSC) - Hazardous category D",
566 "High speed craft (HSC) - Reserved for future use",
567 "High speed craft (HSC) - Reserved for future use",
568 "High speed craft (HSC) - Reserved for future use",
569 "High speed craft (HSC) - Reserved for future use",
570 "High speed craft (HSC) - No additional information",
572 "Search and Rescue vessel",
575 "Anti-pollution equipment",
577 "Spare - Local Vessel",
578 "Spare - Local Vessel",
580 "Ship according to RR Resolution No. 18",
581 "Passenger - all ships of this type",
582 "Passenger - Hazardous category A",
583 "Passenger - Hazardous category B",
584 "Passenger - Hazardous category C",
585 "Passenger - Hazardous category D",
586 "Passenger - Reserved for future use",
587 "Passenger - Reserved for future use",
588 "Passenger - Reserved for future use",
589 "Passenger - Reserved for future use",
590 "Passenger - No additional information",
591 "Cargo - all ships of this type",
592 "Cargo - Hazardous category A",
593 "Cargo - Hazardous category B",
594 "Cargo - Hazardous category C",
595 "Cargo - Hazardous category D",
596 "Cargo - Reserved for future use",
597 "Cargo - Reserved for future use",
598 "Cargo - Reserved for future use",
599 "Cargo - Reserved for future use",
600 "Cargo - No additional information",
601 "Tanker - all ships of this type",
602 "Tanker - Hazardous category A",
603 "Tanker - Hazardous category B",
604 "Tanker - Hazardous category C",
605 "Tanker - Hazardous category D",
606 "Tanker - Reserved for future use",
607 "Tanker - Reserved for future use",
608 "Tanker - Reserved for future use",
609 "Tanker - Reserved for future use",
610 "Tanker - No additional information",
611 "Other Type - all ships of this type",
612 "Other Type - Hazardous category A",
613 "Other Type - Hazardous category B",
614 "Other Type - Hazardous category C",
615 "Other Type - Hazardous category D",
616 "Other Type - Reserved for future use",
617 "Other Type - Reserved for future use",
618 "Other Type - Reserved for future use",
619 "Other Type - Reserved for future use",
620 "Other Type - no additional information",
623 #define SHIPTYPE_DISPLAY(n) (((n) < (unsigned int)NITEMS(ship_type_legends)) ? ship_type_legends[n] : "INVALID SHIP TYPE")
625 static char *station_type_legends[16] = {
626 "All types of mobiles",
627 "Reserved for future use",
628 "All types of Class B mobile stations",
629 "SAR airborne mobile station",
630 "Aid to Navigation station",
631 "Class B shipborne mobile station",
632 "Regional use and inland waterways",
633 "Regional use and inland waterways",
634 "Regional use and inland waterways",
635 "Regional use and inland waterways",
636 "Reserved for future use",
637 "Reserved for future use",
638 "Reserved for future use",
639 "Reserved for future use",
640 "Reserved for future use",
641 "Reserved for future use",
644 #define STATIONTYPE_DISPLAY(n) (((n) < (unsigned int)NITEMS(ship_type_legends)) ? station_type_legends[n] : "INVALID STATION TYPE")
646 static char *navaid_type_legends[] = {
650 "Fixed offshore structure",
651 "Spare, Reserved for future use.",
652 "Light, without sectors",
653 "Light, with sectors",
654 "Leading Light Front",
655 "Leading Light Rear",
656 "Beacon, Cardinal N",
657 "Beacon, Cardinal E",
658 "Beacon, Cardinal S",
659 "Beacon, Cardinal W",
661 "Beacon, Starboard hand",
662 "Beacon, Preferred Channel port hand",
663 "Beacon, Preferred Channel starboard hand",
664 "Beacon, Isolated danger",
665 "Beacon, Safe water",
666 "Beacon, Special mark",
672 "Starboard hand Mark",
673 "Preferred Channel Port hand",
674 "Preferred Channel Starboard hand",
678 "Light Vessel / LANBY / Rigs",
681 #define NAVAIDTYPE_DISPLAY(n) (((n) < (unsigned int)NITEMS(navaid_type_legends[0])) ? navaid_type_legends[n] : "INVALID NAVAID TYPE")
683 #define JSON_BOOL(x) ((x)?"true":"false")
684 (void)snprintf(buf, buflen,
685 "{\"class\":\"AIS\",\"type\":%u,\"repeat\":%u,"
686 "\"mmsi\":%u,\"scaled\":%s,",
687 ais->type, ais->repeat, ais->mmsi, JSON_BOOL(scaled));
688 /*@ -formatcode -mustfreefresh @*/
690 case 1: /* Position Report */
695 char speedlegend[20];
698 * Express turn as nan if not available,
699 * "fastleft"/"fastright" for fast turns.
701 if (ais->type1.turn == -128)
702 (void)strlcpy(turnlegend, "\"nan\"", sizeof(turnlegend));
703 else if (ais->type1.turn == -127)
704 (void)strlcpy(turnlegend, "\"fastleft\"", sizeof(turnlegend));
705 else if (ais->type1.turn == 127)
706 (void)strlcpy(turnlegend, "\"fastright\"",
709 double rot1 = ais->type1.turn / 4.733;
710 (void)snprintf(turnlegend, sizeof(turnlegend),
711 "%.0f", rot1 * rot1);
715 * Express speed as nan if not available,
716 * "fast" for fast movers.
718 if (ais->type1.speed == AIS_SPEED_NOT_AVAILABLE)
719 (void)strlcpy(speedlegend, "\"nan\"", sizeof(speedlegend));
720 else if (ais->type1.speed == AIS_SPEED_FAST_MOVER)
721 (void)strlcpy(speedlegend, "\"fast\"", sizeof(speedlegend));
723 (void)snprintf(speedlegend, sizeof(speedlegend),
724 "%.1f", ais->type1.speed / 10.0);
726 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
727 "\"status\":\"%s\",\"turn\":%s,\"speed\":%s,"
728 "\"accuracy\":%s,\"lon\":%.4f,\"lat\":%.4f,"
729 "\"course\":%u,\"heading\":%u,\"second\":%u,"
730 "\"maneuver\":%u,\"raim\":%s,\"radio\":%u}\r\n",
731 nav_legends[ais->type1.status],
734 JSON_BOOL(ais->type1.accuracy),
735 ais->type1.lon / AIS_LATLON_SCALE,
736 ais->type1.lat / AIS_LATLON_SCALE,
741 JSON_BOOL(ais->type1.raim), ais->type1.radio);
743 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
744 "\"status\":%u,\"turn\":%d,\"speed\":%u,"
745 "\"accuracy\":%s,\"lon\":%d,\"lat\":%d,"
746 "\"course\":%u,\"heading\":%u,\"second\":%u,"
747 "\"maneuver\":%u,\"raim\":%s,\"radio\":%u}\r\n",
751 JSON_BOOL(ais->type1.accuracy),
758 JSON_BOOL(ais->type1.raim), ais->type1.radio);
761 case 4: /* Base Station Report */
762 case 11: /* UTC/Date Response */
764 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
765 "\"timestamp\":\"%4u:%02u:%02uT%02u:%02u:%02uZ\","
766 "\"accuracy\":%s,\"lon\":%.4f,\"lat\":%.4f,"
767 "\"epfd\":\"%s\",\"raim\":%s,\"radio\":%u}\r\n",
774 JSON_BOOL(ais->type4.accuracy),
775 ais->type4.lon / AIS_LATLON_SCALE,
776 ais->type4.lat / AIS_LATLON_SCALE,
777 epfd_legends[ais->type4.epfd],
778 JSON_BOOL(ais->type4.raim), ais->type4.radio);
780 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
781 "\"timestamp\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\","
782 "\"accuracy\":%s,\"lon\":%d,\"lat\":%d,"
783 "\"epfd\":%u,\"raim\":%s,\"radio\":%u}\r\n",
790 JSON_BOOL(ais->type4.accuracy),
794 JSON_BOOL(ais->type4.raim), ais->type4.radio);
797 case 5: /* Ship static and voyage related data */
800 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
801 "\"imo\":%u,\"ais_version\":%u,\"callsign\":\"%s\","
802 "\"shipname\":\"%s\",\"shiptype\":\"%s\","
803 "\"to_bow\":%u,\"to_stern\":%u,\"to_port\":%u,"
804 "\"to_starboard\":%u,\"epfd\":\"%s\","
805 "\"eta\":\"%02u-%02uT%02u:%02uZ\","
806 "\"draught\":%.1f,\"destination\":\"%s\","
809 ais->type5.ais_version,
810 json_stringify(buf1, sizeof(buf1),
811 ais->type5.callsign),
812 json_stringify(buf2, sizeof(buf2),
813 ais->type5.shipname),
814 SHIPTYPE_DISPLAY(ais->type5.shiptype),
815 ais->type5.to_bow, ais->type5.to_stern,
816 ais->type5.to_port, ais->type5.to_starboard,
817 epfd_legends[ais->type5.epfd], ais->type5.month,
818 ais->type5.day, ais->type5.hour, ais->type5.minute,
819 ais->type5.draught / 10.0,
820 json_stringify(buf3, sizeof(buf3),
821 ais->type5.destination),
825 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
826 "\"imo\":%u,\"ais_version\":%u,\"callsign\":\"%s\","
827 "\"shipname\":\"%s\",\"shiptype\":%u,"
828 "\"to_bow\":%u,\"to_stern\":%u,\"to_port\":%u,"
829 "\"to_starboard\":%u,\"epfd\":%u,"
830 "\"eta\":\"%02u-%02uT%02u:%02uZ\","
831 "\"draught\":%u,\"destination\":\"%s\","
834 ais->type5.ais_version,
835 json_stringify(buf1, sizeof(buf1),
836 ais->type5.callsign),
837 json_stringify(buf2, sizeof(buf2),
838 ais->type5.shipname),
839 ais->type5.shiptype, ais->type5.to_bow,
840 ais->type5.to_stern, ais->type5.to_port,
841 ais->type5.to_starboard, ais->type5.epfd,
842 ais->type5.month, ais->type5.day, ais->type5.hour,
843 ais->type5.minute, ais->type5.draught,
844 json_stringify(buf3, sizeof(buf3),
845 ais->type5.destination),
849 case 6: /* Binary Message */
850 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
851 "\"seqno\":%u,\"dest_mmsi\":%u,"
852 "\"retransmit\":%s,\"dac\":%u,\"fid\":%u,"
853 "\"data\":\"%zd:%s\"}\r\n",
855 ais->type6.dest_mmsi,
856 JSON_BOOL(ais->type6.retransmit),
860 gpsd_hexdump(ais->type6.bitdata,
861 (ais->type6.bitcount + 7) / 8));
863 case 7: /* Binary Acknowledge */
864 case 13: /* Safety Related Acknowledge */
865 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
866 "\"mmsi1\":%u,\"mmsi2\":%u,\"mmsi3\":%u,\"mmsi4\":%u}\r\n",
868 ais->type7.mmsi2, ais->type7.mmsi3, ais->type7.mmsi4);
870 case 8: /* Binary Broadcast Message */
871 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
872 "\"dac\":%u,\"fid\":%u,\"data\":\"%zd:%s\"}\r\n",
876 gpsd_hexdump(ais->type8.bitdata,
877 (ais->type8.bitcount + 7) / 8));
879 case 9: /* Standard SAR Aircraft Position Report */
882 char speedlegend[20];
885 * Express altitude as nan if not available,
886 * "high" for above the reporting ceiling.
888 if (ais->type9.alt == AIS_ALT_NOT_AVAILABLE)
889 (void)strlcpy(altlegend, "\"nan\"", sizeof(altlegend));
890 else if (ais->type9.alt == AIS_ALT_HIGH)
891 (void)strlcpy(altlegend, "\"high\"", sizeof(altlegend));
893 (void)snprintf(altlegend, sizeof(altlegend),
894 "%.1f", ais->type9.alt / 10.0);
897 * Express speed as nan if not available,
898 * "high" for above the reporting ceiling.
900 if (ais->type9.speed == AIS_SAR_SPEED_NOT_AVAILABLE)
901 (void)strlcpy(speedlegend, "\"nan\"", sizeof(speedlegend));
902 else if (ais->type9.speed == AIS_SAR_FAST_MOVER)
903 (void)strlcpy(speedlegend, "\"fast\"", sizeof(speedlegend));
905 (void)snprintf(speedlegend, sizeof(speedlegend),
906 "%.1f", ais->type1.speed / 10.0);
908 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
909 "\"alt\":%s,\"speed\":%s,\"accuracy\":%s,"
910 "\"lon\":%.4f,\"lat\":%.4f,\"course\":%.1f,"
911 "\"second\":%u,\"regional\":%u,\"dte\":%u,"
912 "\"raim\":%s,\"radio\":%u}\r\n",
915 JSON_BOOL(ais->type9.accuracy),
916 ais->type9.lon / AIS_LATLON_SCALE,
917 ais->type9.lat / AIS_LATLON_SCALE,
918 ais->type9.course / 10.0,
922 JSON_BOOL(ais->type9.raim), ais->type9.radio);
924 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
925 "\"alt\":%u,\"speed\":%u,\"accuracy\":%s,"
926 "\"lon\":%d,\"lat\":%d,\"course\":%u,"
927 "\"second\":%u,\"regional\":%u,\"dte\":%u,"
928 "\"raim\":%s,\"radio\":%u}\r\n",
931 JSON_BOOL(ais->type9.accuracy),
938 JSON_BOOL(ais->type9.raim), ais->type9.radio);
941 case 10: /* UTC/Date Inquiry */
942 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
943 "\"dest_mmsi\":%u}\r\n", ais->type10.dest_mmsi);
945 case 12: /* Safety Related Message */
946 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
947 "\"seqno\":%u,\"dest_mmsi\":%u,\"retransmit\":%s,\"text\":\"%s\"}\r\n",
949 ais->type12.dest_mmsi,
950 JSON_BOOL(ais->type12.retransmit),
951 json_stringify(buf1, sizeof(buf1), ais->type12.text));
953 case 14: /* Safety Related Broadcast Message */
954 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
955 "\"text\":\"%s\"}\r\n",
956 json_stringify(buf1, sizeof(buf1), ais->type14.text));
958 case 15: /* Interrogation */
959 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
960 "\"mmsi1\":%u,\"type1_1\":%u,\"offset1_1\":%u,"
961 "\"type1_2\":%u,\"offset1_2\":%u,\"mmsi2\":%u,"
962 "\"type2_1\":%u,\"offset2_1\":%u}\r\n",
965 ais->type15.offset1_1,
967 ais->type15.offset1_2,
969 ais->type15.type2_1, ais->type15.offset2_1);
972 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
973 "\"mmsi1\":%u,\"offset1\":%u,\"increment1\":%u,"
974 "\"mmsi2\":%u,\"offset2\":%u,\"increment2\":%u}\r\n",
977 ais->type16.increment1,
979 ais->type16.offset2, ais->type16.increment2);
983 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
984 "\"lon\":%.1f,\"lat\":%.1f,\"data\":\"%zd:%s\"}\r\n",
985 ais->type17.lon / AIS_GNSS_LATLON_SCALE,
986 ais->type17.lat / AIS_GNSS_LATLON_SCALE,
987 ais->type17.bitcount,
988 gpsd_hexdump(ais->type17.bitdata,
989 (ais->type17.bitcount + 7) / 8));
991 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
992 "\"lon\":%d,\"lat\":%d,\"data\":\"%zd:%s\"}\r\n",
995 ais->type17.bitcount,
996 gpsd_hexdump(ais->type17.bitdata,
997 (ais->type17.bitcount + 7) / 8));
1002 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1003 "\"reserved\":%u,\"speed\":%.1f,\"accuracy\":%s,"
1004 "\"lon\":%.4f,\"lat\":%.4f,\"course\":%.1f,"
1005 "\"heading\":%u,\"second\":%u,\"regional\":%u,"
1006 "\"cs\":%s,\"display\":%s,\"dsc\":%s,\"band\":%s,"
1007 "\"msg22\":%s,\"raim\":%s,\"radio\":%u}\r\n",
1008 ais->type18.reserved,
1009 ais->type18.speed / 10.0,
1010 JSON_BOOL(ais->type18.accuracy),
1011 ais->type18.lon / AIS_LATLON_SCALE,
1012 ais->type18.lat / AIS_LATLON_SCALE,
1013 ais->type18.course / 10.0,
1014 ais->type18.heading,
1016 ais->type18.regional,
1017 JSON_BOOL(ais->type18.cs),
1018 JSON_BOOL(ais->type18.display),
1019 JSON_BOOL(ais->type18.dsc),
1020 JSON_BOOL(ais->type18.band),
1021 JSON_BOOL(ais->type18.msg22),
1022 JSON_BOOL(ais->type18.raim), ais->type18.radio);
1024 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1025 "\"reserved\":%u,\"speed\":%u,\"accuracy\":%s,"
1026 "\"lon\":%d,\"lat\":%d,\"course\":%u,"
1027 "\"heading\":%u,\"second\":%u,\"regional\":%u,"
1028 "\"cs\":%s,\"display\":%s,\"dsc\":%s,\"band\":%s,"
1029 "\"msg22\":%s,\"raim\":%s,\"radio\":%u}\r\n",
1030 ais->type18.reserved,
1032 JSON_BOOL(ais->type18.accuracy),
1036 ais->type18.heading,
1038 ais->type18.regional,
1039 JSON_BOOL(ais->type18.cs),
1040 JSON_BOOL(ais->type18.display),
1041 JSON_BOOL(ais->type18.dsc),
1042 JSON_BOOL(ais->type18.band),
1043 JSON_BOOL(ais->type18.msg22),
1044 JSON_BOOL(ais->type18.raim), ais->type18.radio);
1049 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1050 "\"reserved\":%u,\"speed\":%.1f,\"accuracy\":%s,"
1051 "\"lon\":%.4f,\"lat\":%.4f,\"course\":%.1f,"
1052 "\"heading\":%u,\"second\":%u,\"regional\":%u,"
1053 "\"shipname\":\"%s\",\"shiptype\":\"%s\","
1054 "\"to_bow\":%u,\"to_stern\":%u,\"to_port\":%u,"
1055 "\"to_starboard\":%u,\"epfd\":\"%s\",\"raim\":%s,"
1056 "\"dte\":%u,\"assigned\":%s}\r\n",
1057 ais->type19.reserved,
1058 ais->type19.speed / 10.0,
1059 JSON_BOOL(ais->type19.accuracy),
1060 ais->type19.lon / AIS_LATLON_SCALE,
1061 ais->type19.lat / AIS_LATLON_SCALE,
1062 ais->type19.course / 10.0,
1063 ais->type19.heading,
1065 ais->type19.regional,
1066 ais->type19.shipname,
1067 SHIPTYPE_DISPLAY(ais->type19.shiptype),
1069 ais->type19.to_stern,
1070 ais->type19.to_port,
1071 ais->type19.to_starboard,
1072 epfd_legends[ais->type19.epfd],
1073 JSON_BOOL(ais->type19.raim),
1074 ais->type19.dte, JSON_BOOL(ais->type19.assigned));
1076 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1077 "\"reserved\":%u,\"speed\":%u,\"accuracy\":%s,"
1078 "\"lon\":%d,\"lat\":%d,\"course\":%u,"
1079 "\"heading\":%u,\"second\":%u,\"regional\":%u,"
1080 "\"shipname\":\"%s\",\"shiptype\":%u,"
1081 "\"to_bow\":%u,\"to_stern\":%u,\"to_port\":%u,"
1082 "\"to_starboard\":%u,\"epfd\":%u,\"raim\":%s,"
1083 "\"dte\":%u,\"assigned\":%s}\r\n",
1084 ais->type19.reserved,
1086 JSON_BOOL(ais->type19.accuracy),
1090 ais->type19.heading,
1092 ais->type19.regional,
1093 ais->type19.shipname,
1094 ais->type19.shiptype,
1096 ais->type19.to_stern,
1097 ais->type19.to_port,
1098 ais->type19.to_starboard,
1100 JSON_BOOL(ais->type19.raim),
1101 ais->type19.dte, JSON_BOOL(ais->type19.assigned));
1104 case 20: /* Data Link Management Message */
1105 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1106 "\"offset1\":%u,\"number1\":%u,"
1107 "\"timeout1\":%u,\"increment1\":%u,"
1108 "\"offset2\":%u,\"number2\":%u,"
1109 "\"timeout2\":%u,\"increment2\":%u,"
1110 "\"offset3\":%u,\"number3\":%u,"
1111 "\"timeout3\":%u,\"increment3\":%u,"
1112 "\"offset4\":%u,\"number4\":%u,"
1113 "\"timeout4\":%u,\"increment4\":%u}\r\n",
1114 ais->type20.offset1,
1115 ais->type20.number1,
1116 ais->type20.timeout1,
1117 ais->type20.increment1,
1118 ais->type20.offset2,
1119 ais->type20.number2,
1120 ais->type20.timeout2,
1121 ais->type20.increment2,
1122 ais->type20.offset3,
1123 ais->type20.number3,
1124 ais->type20.timeout3,
1125 ais->type20.increment3,
1126 ais->type20.offset4,
1127 ais->type20.number4,
1128 ais->type20.timeout4, ais->type20.increment4);
1130 case 21: /* Aid to Navigation */
1132 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1133 "\"aid_type\":\"%s\",\"name\":\"%s\",\"lon\":%.4f,"
1134 "\"lat\":%.4f,\"accuracy\":%s,\"to_bow\":%u,"
1135 "\"to_stern\":%u,\"to_port\":%u,"
1136 "\"to_starboard\":%u,\"epfd\":\"%s\","
1137 "\"second\":%u,\"regional\":%u,"
1138 "\"off_position\":%s,\"raim\":%s,"
1139 "\"virtual_aid\":%s}\r\n",
1140 NAVAIDTYPE_DISPLAY(ais->type21.aid_type),
1141 json_stringify(buf1, sizeof(buf1),
1143 ais->type21.lon / AIS_LATLON_SCALE,
1144 ais->type21.lat / AIS_LATLON_SCALE,
1145 JSON_BOOL(ais->type21.accuracy),
1146 ais->type21.to_bow, ais->type21.to_stern,
1147 ais->type21.to_port, ais->type21.to_starboard,
1148 epfd_legends[ais->type21.epfd], ais->type21.second,
1149 ais->type21.regional,
1150 JSON_BOOL(ais->type21.off_position),
1151 JSON_BOOL(ais->type21.raim),
1152 JSON_BOOL(ais->type21.virtual_aid));
1154 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1155 "\"aid_type\":%u,\"name\":\"%s\",\"accuracy\":%s,"
1156 "\"lon\":%d,\"lat\":%d,\"to_bow\":%u,"
1157 "\"to_stern\":%u,\"to_port\":%u,\"to_starboard\":%u,"
1158 "\"epfd\":%u,\"second\":%u,\"regional\":%u,"
1159 "\"off_position\":%s,\"raim\":%s,"
1160 "\"virtual_aid\":%s}\r\n",
1161 ais->type21.aid_type,
1163 JSON_BOOL(ais->type21.accuracy),
1167 ais->type21.to_stern,
1168 ais->type21.to_port,
1169 ais->type21.to_starboard,
1172 ais->type21.regional,
1173 JSON_BOOL(ais->type21.off_position),
1174 JSON_BOOL(ais->type21.raim),
1175 JSON_BOOL(ais->type21.virtual_aid));
1178 case 22: /* Channel Management */
1179 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1180 "\"channel_a\":%u,\"channel_b\":%u,"
1181 "\"txrx\":%u,\"power\":%s,",
1182 ais->type22.channel_a,
1183 ais->type22.channel_b,
1184 ais->type22.txrx, JSON_BOOL(ais->type22.power));
1185 if (ais->type22.addressed) {
1186 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1187 "\"dest1\":%u,\"dest2\":%u",
1188 ais->type22.mmsi.dest1, ais->type22.mmsi.dest2);
1189 } else if (scaled) {
1190 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1191 "\"ne_lon\":\"%f\",\"ne_lat\":\"%f\","
1192 "\"sw_lon\":\"%f\",\"sw_lat\":\"%f\",",
1193 ais->type22.area.ne_lon / AIS_CHANNEL_LATLON_SCALE,
1194 ais->type22.area.ne_lat / AIS_CHANNEL_LATLON_SCALE,
1195 ais->type22.area.sw_lon / AIS_CHANNEL_LATLON_SCALE,
1196 ais->type22.area.sw_lat /
1197 AIS_CHANNEL_LATLON_SCALE);
1199 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1200 "\"ne_lon\":%d,\"ne_lat\":%d,"
1201 "\"sw_lon\":%d,\"sw_lat\":%d,",
1202 ais->type22.area.ne_lon,
1203 ais->type22.area.ne_lat,
1204 ais->type22.area.sw_lon, ais->type22.area.sw_lat);
1206 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1207 "\"addressed\":%s,\"band_a\":%s,"
1208 "\"band_b\":%s,\"zonesize\":%u}\r\n",
1209 JSON_BOOL(ais->type22.addressed),
1210 JSON_BOOL(ais->type22.band_a),
1211 JSON_BOOL(ais->type22.band_b), ais->type22.zonesize);
1213 case 23: /* Group Assignment Command */
1215 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1216 "\"ne_lon\":\"%f\",\"ne_lat\":\"%f\","
1217 "\"sw_lon\":\"%f\",\"sw_lat\":\"%f\","
1218 "\"stationtype\":\"%s\",\"shiptype\":\"%s\","
1219 "\"interval\":%u,\"quiet\":%u}\r\n",
1220 ais->type23.ne_lon / AIS_CHANNEL_LATLON_SCALE,
1221 ais->type23.ne_lat / AIS_CHANNEL_LATLON_SCALE,
1222 ais->type23.sw_lon / AIS_CHANNEL_LATLON_SCALE,
1223 ais->type23.sw_lat / AIS_CHANNEL_LATLON_SCALE,
1224 STATIONTYPE_DISPLAY(ais->type23.stationtype),
1225 SHIPTYPE_DISPLAY(ais->type23.shiptype),
1226 ais->type23.interval, ais->type23.quiet);
1228 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1229 "\"ne_lon\":%d,\"ne_lat\":%d,"
1230 "\"sw_lon\":%d,\"sw_lat\":%d,"
1231 "\"stationtype\":%u,\"shiptype\":%u,"
1232 "\"interval\":%u,\"quiet\":%u}\r\n",
1237 ais->type23.stationtype,
1238 ais->type23.shiptype,
1239 ais->type23.interval, ais->type23.quiet);
1242 case 24: /* Class B CS Static Data Report */
1243 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1244 "\"shipname\":\"%s\",",
1245 json_stringify(buf1, sizeof(buf1),
1246 ais->type24.shipname));
1248 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1249 "\"shiptype\":\"%s\",",
1250 SHIPTYPE_DISPLAY(ais->type24.shiptype));
1252 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1253 "\"shiptype\":%u,", ais->type24.shiptype);
1255 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1256 "\"vendorid\":\"%s\",\"callsign\":\"%s\",",
1257 ais->type24.vendorid, ais->type24.callsign);
1258 if (AIS_AUXILIARY_MMSI(ais->mmsi)) {
1259 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1260 "mothership_\"mmsi\":%u}\r\n",
1261 ais->type24.mothership_mmsi);
1263 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1264 "\"to_bow\":%u,\"to_stern\":%u,"
1265 "\"to_port\":%u,\"to_starboard\":%u}\r\n",
1266 ais->type24.dim.to_bow,
1267 ais->type24.dim.to_stern,
1268 ais->type24.dim.to_port,
1269 ais->type24.dim.to_starboard);
1272 case 25: /* Binary Message, Single Slot */
1273 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1274 "\"addressed\":%s,\"structured\":%s,\"dest_mmsi\":%u,"
1275 "\"app_id\":%u,\"data\":\"%zd:%s\"}\r\n",
1276 JSON_BOOL(ais->type25.addressed),
1277 JSON_BOOL(ais->type25.structured),
1278 ais->type25.dest_mmsi,
1280 ais->type25.bitcount,
1281 gpsd_hexdump(ais->type25.bitdata,
1282 (ais->type25.bitcount + 7) / 8));
1284 case 26: /* Binary Message, Multiple Slot */
1285 (void)snprintf(buf + strlen(buf), buflen - strlen(buf),
1286 "\"addressed\":%s,\"structured\":%s,\"dest_mmsi\":%u,"
1287 "\"app_id\":%u,\"data\":\"%zd:%s\"\"radio\":%u}\r\n",
1288 JSON_BOOL(ais->type26.addressed),
1289 JSON_BOOL(ais->type26.structured),
1290 ais->type26.dest_mmsi,
1292 ais->type26.bitcount,
1293 gpsd_hexdump(ais->type26.bitdata,
1294 (ais->type26.bitcount + 7) / 8),
1298 if (buf[strlen(buf) - 1] == ',')
1299 buf[strlen(buf) - 1] = '\0';
1300 (void)strlcat(buf, "}\r\n", buflen);
1303 /*@ +formatcode +mustfreefresh @*/
1306 #endif /* defined(AIVDM_ENABLE) */
1308 #ifdef COMPASS_ENABLE
1309 void json_att_dump(const struct gps_data_t *gpsdata,
1310 /*@out@*/ char *reply, size_t replylen)
1311 /* dump the contents of an attitude_t structure as JSON */
1313 assert(replylen > 2);
1314 (void)strlcpy(reply, "{\"class\":\"ATT\",", replylen);
1315 (void)snprintf(reply + strlen(reply),
1316 replylen - strlen(reply),
1318 gpsdata->tag[0] != '\0' ? gpsdata->tag : "-");
1319 (void)snprintf(reply + strlen(reply),
1320 replylen - strlen(reply),
1321 "\"device\":\"%s\",", gpsdata->dev.path);
1322 if (isnan(gpsdata->attitude.heading) == 0) {
1323 (void)snprintf(reply + strlen(reply),
1324 replylen - strlen(reply),
1325 "\"heading\":%.2f,", gpsdata->attitude.heading);
1326 if (gpsdata->attitude.mag_st != '\0')
1327 (void)snprintf(reply + strlen(reply),
1328 replylen - strlen(reply),
1329 "\"mag_st\":\"%c\",", gpsdata->attitude.mag_st);
1332 if (isnan(gpsdata->attitude.pitch) == 0) {
1333 (void)snprintf(reply + strlen(reply),
1334 replylen - strlen(reply),
1335 "\"pitch\":%.2f,", gpsdata->attitude.pitch);
1336 if (gpsdata->attitude.pitch_st != '\0')
1337 (void)snprintf(reply + strlen(reply),
1338 replylen - strlen(reply),
1339 "\"pitch_st\":\"%c\",",
1340 gpsdata->attitude.pitch_st);
1343 if (isnan(gpsdata->attitude.yaw) == 0) {
1344 (void)snprintf(reply + strlen(reply),
1345 replylen - strlen(reply),
1346 "\"yaw\":%.2f,", gpsdata->attitude.yaw);
1347 if (gpsdata->attitude.yaw_st != '\0')
1348 (void)snprintf(reply + strlen(reply),
1349 replylen - strlen(reply),
1350 "\"yaw_st\":\"%c\",", gpsdata->attitude.yaw_st);
1353 if (isnan(gpsdata->attitude.roll) == 0) {
1354 (void)snprintf(reply + strlen(reply),
1355 replylen - strlen(reply),
1356 "\"roll\":%.2f,", gpsdata->attitude.roll);
1357 if (gpsdata->attitude.roll_st != '\0')
1358 (void)snprintf(reply + strlen(reply),
1359 replylen - strlen(reply),
1360 "\"roll_st\":\"%c\",", gpsdata->attitude.roll_st);
1363 if (isnan(gpsdata->attitude.yaw) == 0) {
1364 (void)snprintf(reply + strlen(reply),
1365 replylen - strlen(reply),
1366 "\"yaw\":%.2f,", gpsdata->attitude.yaw);
1367 if (gpsdata->attitude.yaw_st != '\0')
1368 (void)snprintf(reply + strlen(reply),
1369 replylen - strlen(reply),
1370 "\"yaw_st\":\"%c\",", gpsdata->attitude.yaw_st);
1373 if (isnan(gpsdata->attitude.dip) == 0)
1374 (void)snprintf(reply + strlen(reply),
1375 replylen - strlen(reply),
1376 "\"dip\":%.3f,", gpsdata->attitude.dip);
1378 if (isnan(gpsdata->attitude.mag_len) == 0)
1379 (void)snprintf(reply + strlen(reply),
1380 replylen - strlen(reply),
1381 "\"mag_len\":%.3f,", gpsdata->attitude.mag_len);
1382 if (isnan(gpsdata->attitude.mag_x) == 0)
1383 (void)snprintf(reply + strlen(reply),
1384 replylen - strlen(reply),
1385 "\"mag_x\":%.3f,", gpsdata->attitude.mag_x);
1386 if (isnan(gpsdata->attitude.mag_y) == 0)
1387 (void)snprintf(reply + strlen(reply),
1388 replylen - strlen(reply),
1389 "\"mag_y\":%.3f,", gpsdata->attitude.mag_y);
1390 if (isnan(gpsdata->attitude.mag_z) == 0)
1391 (void)snprintf(reply + strlen(reply),
1392 replylen - strlen(reply),
1393 "\"mag_z\":%.3f,", gpsdata->attitude.mag_z);
1395 if (isnan(gpsdata->attitude.acc_len) == 0)
1396 (void)snprintf(reply + strlen(reply),
1397 replylen - strlen(reply),
1398 "\"acc_len\":%.3f,", gpsdata->attitude.acc_len);
1399 if (isnan(gpsdata->attitude.acc_x) == 0)
1400 (void)snprintf(reply + strlen(reply),
1401 replylen - strlen(reply),
1402 "\"acc_x\":%.3f,", gpsdata->attitude.acc_x);
1403 if (isnan(gpsdata->attitude.acc_y) == 0)
1404 (void)snprintf(reply + strlen(reply),
1405 replylen - strlen(reply),
1406 "\"acc_y\":%.3f,", gpsdata->attitude.acc_y);
1407 if (isnan(gpsdata->attitude.acc_z) == 0)
1408 (void)snprintf(reply + strlen(reply),
1409 replylen - strlen(reply),
1410 "\"acc_z\":%.3f,", gpsdata->attitude.acc_z);
1412 if (isnan(gpsdata->attitude.gyro_x) == 0)
1413 (void)snprintf(reply + strlen(reply),
1414 replylen - strlen(reply),
1415 "\"gyro_x\":%.3f,", gpsdata->attitude.gyro_x);
1416 if (isnan(gpsdata->attitude.gyro_y) == 0)
1417 (void)snprintf(reply + strlen(reply),
1418 replylen - strlen(reply),
1419 "\"gyro_y\":%.3f,", gpsdata->attitude.gyro_y);
1421 if (isnan(gpsdata->attitude.temp) == 0)
1422 (void)snprintf(reply + strlen(reply),
1423 replylen - strlen(reply),
1424 "\"temp\":%.3f,", gpsdata->attitude.temp);
1425 if (isnan(gpsdata->attitude.depth) == 0)
1426 (void)snprintf(reply + strlen(reply),
1427 replylen - strlen(reply),
1428 "\"depth\":%.3f,", gpsdata->attitude.depth);
1430 if (reply[strlen(reply) - 1] == ',')
1431 reply[strlen(reply) - 1] = '\0'; /* trim trailing comma */
1432 (void)strlcat(reply, "}\r\n", replylen);
1434 #endif /* COMPASS_ENABLE */
1437 /* gpsd_json.c ends here */