diff options
-rw-r--r-- | ais_json.c | 14 | ||||
-rwxr-xr-x | devtools/ais.py | 2 | ||||
-rwxr-xr-x | devtools/tablegen.py | 2 | ||||
-rw-r--r-- | driver_ais.c | 24 | ||||
-rw-r--r-- | gpsd_json.c | 157 | ||||
-rw-r--r-- | gpsdecode.c | 16 | ||||
-rw-r--r-- | jsongen.py.in | 5 | ||||
-rw-r--r-- | test/sample.aivdm | 88 | ||||
-rw-r--r-- | test/sample.aivdm.chk | 24 | ||||
-rw-r--r-- | test/sample.aivdm.js.chk | 26 | ||||
-rw-r--r-- | test/sample.aivdm.ju.chk | 24 | ||||
-rw-r--r-- | www/AIVDM.txt | 2 |
12 files changed, 319 insertions, 65 deletions
@@ -97,8 +97,9 @@ int json_ais_read(const char *buf, ais->type4.hour = AIS_HOUR_NOT_AVAILABLE; ais->type4.minute = AIS_MINUTE_NOT_AVAILABLE; ais->type4.second = AIS_SECOND_NOT_AVAILABLE; + // We use %u for the date to allow for dodgy years (>9999) to go through // cppcheck-suppress uninitvar - (void)sscanf(timestamp, "%4u-%02u-%02uT%02u:%02u:%02uZ", + (void)sscanf(timestamp, "%u-%02u-%02uT%02u:%02u:%02uZ", &ais->type4.year, &ais->type4.month, &ais->type4.day, @@ -157,14 +158,15 @@ int json_ais_read(const char *buf, status = json_read_object(buf, json_ais6_fid16, endptr); imo = true; } - else if (strstr(buf, "\"fid\":18,") != NULL || strstr(buf, "\"fid\":11,") != NULL) { + else if (strstr(buf, "\"fid\":18,") != NULL) { status = json_read_object(buf, json_ais6_fid18, endptr); if (status == 0) { ais->type6.dac1fid18.day = AIS_DAY_NOT_AVAILABLE; ais->type6.dac1fid18.hour = AIS_HOUR_NOT_AVAILABLE; ais->type6.dac1fid18.minute = AIS_MINUTE_NOT_AVAILABLE; // cppcheck-suppress uninitvar - (void)sscanf(arrival, "%02uT%02u:%02uZ", + (void)sscanf(arrival, "%02u-%02uT%02u:%02uZ", + &ais->type6.dac1fid18.month, &ais->type6.dac1fid18.day, &ais->type6.dac1fid18.hour, &ais->type6.dac1fid18.minute); @@ -360,8 +362,14 @@ int json_ais_read(const char *buf, status = json_read_object(buf, json_ais24, endptr); } else if (strstr(buf, "\"type\":25,") != NULL) { status = json_read_object(buf, json_ais25, endptr); + if (status == 0) + lenhex_unpack(data, &ais->type25.bitcount, + ais->type25.bitdata, sizeof(ais->type25.bitdata)); } else if (strstr(buf, "\"type\":26,") != NULL) { status = json_read_object(buf, json_ais26, endptr); + if (status == 0) + lenhex_unpack(data, &ais->type26.bitcount, + ais->type26.bitdata, sizeof(ais->type26.bitdata)); } else if (strstr(buf, "\"type\":27,") != NULL) { status = json_read_object(buf, json_ais27, endptr); } else { diff --git a/devtools/ais.py b/devtools/ais.py index a2ea58ac..f9f5706d 100755 --- a/devtools/ais.py +++ b/devtools/ais.py @@ -864,7 +864,7 @@ type27 = ( aivdm_decode = ( bitfield('msgtype', 6, 'unsigned', 0, "Message Type", - validator=lambda n: n > 0 and n <= 26), + validator=lambda n: n > 0 and n <= 27), bitfield('repeat', 2, 'unsigned', None, "Repeat Indicator"), bitfield('mmsi', 30, 'unsigned', 0, "MMSI"), # This is the master dispatch on AIS message type diff --git a/devtools/tablegen.py b/devtools/tablegen.py index 4a68b8df..c0b3cba8 100755 --- a/devtools/tablegen.py +++ b/devtools/tablegen.py @@ -327,7 +327,7 @@ def make_json_dumper(wfp): print >>wfp, base + "}" print >>wfp, base + "if (buf[strlen(buf) - 1] == ',')" print >>wfp, base + step + "buf[strlen(buf)-1] = '\0';" - print >>wfp, base + "(void)strlcat(buf, ']}\r\n,', buflen - strlen(buf));" + print >>wfp, base + "(void)strlcat(buf, ']}\r\n', buflen - strlen(buf));" def make_json_generator(wfp): # Write a stanza for jsongen.py.in describing how to generate a diff --git a/driver_ais.c b/driver_ais.c index 385a448c..af58fcaa 100644 --- a/driver_ais.c +++ b/driver_ais.c @@ -204,6 +204,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type6.dac1fid12.amount = UBITS(345, 10); ais->type6.dac1fid12.unit = UBITS(355, 2); /* skip 3 bits */ + imo = true; break; case 14: /* IMO236 - Tidal Window */ ais->type6.dac1fid32.month = UBITS(88, 4); @@ -225,9 +226,11 @@ bool ais_binary_decode(struct ais_t *ais, ais->type6.dac1fid32.ntidals = u; #undef ARRAY_BASE #undef ELEMENT_SIZE + imo = true; break; case 15: /* IMO236 - Extended Ship Static and Voyage Related Data */ ais->type6.dac1fid15.airdraught = UBITS(56, 11); + imo = true; break; case 16: /* IMO236 - Number of persons on board */ if (ais->type6.bitcount == 136) @@ -247,6 +250,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type6.dac1fid18.lon = SBITS(268, 25); ais->type6.dac1fid18.lat = SBITS(293, 24); /* skip 43 bits */ + imo = true; break; case 20: /* IMO289 - Berthing data - addressed */ ais->type6.dac1fid20.linkage = UBITS(88, 10); @@ -287,6 +291,7 @@ bool ais_binary_decode(struct ais_t *ais, UCHARS(191, ais->type6.dac1fid20.berth_name); ais->type6.dac1fid20.berth_lon = SBITS(311, 25); ais->type6.dac1fid20.berth_lat = SBITS(336, 24); + imo = true; break; case 23: /* IMO289 - Area notice - addressed */ break; @@ -298,6 +303,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type6.dac1fid25.cargos[u].subtype = UBITS(104+u*17,13); } ais->type6.dac1fid25.ncargos = u; + imo = true; break; case 28: /* IMO289 - Route info - addressed */ ais->type6.dac1fid28.linkage = UBITS(88, 10); @@ -318,12 +324,14 @@ bool ais_binary_decode(struct ais_t *ais, } #undef ARRAY_BASE #undef ELEMENT_SIZE + imo = true; break; case 30: /* IMO289 - Text description - addressed */ ais->type6.dac1fid30.linkage = UBITS(88, 10); from_sixbit((char *)bits, 98, bitlen-98, ais->type6.dac1fid30.text); + imo = true; break; case 32: /* IMO289 - Tidal Window */ ais->type6.dac1fid32.month = UBITS(88, 4); @@ -345,6 +353,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type6.dac1fid32.ntidals = u; #undef ARRAY_BASE #undef ELEMENT_SIZE + imo = true; break; } if (!imo) @@ -443,10 +452,12 @@ bool ais_binary_decode(struct ais_t *ais, ais->type8.dac1fid13.thour = UBITS(457, 5); ais->type8.dac1fid13.tminute = UBITS(462, 6); /* skip 4 bits */ + imo = true; break; case 15: /* IMO236 - Extended ship and voyage */ ais->type8.dac1fid15.airdraught = UBITS(56, 11); /* skip 5 bits */ + imo = true; break; case 17: /* IMO289 - VTS-generated/synthetic targets */ #define ARRAY_BASE 56 @@ -479,6 +490,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type8.dac1fid17.ntargets = u; #undef ARRAY_BASE #undef ELEMENT_SIZE + imo = true; break; case 19: /* IMO289 - Marine Traffic Signal */ ais->type8.dac1fid19.linkage = UBITS(56, 10); @@ -491,6 +503,7 @@ bool ais_binary_decode(struct ais_t *ais, ais->type8.dac1fid19.minute = UBITS(247, 6); ais->type8.dac1fid19.nextsignal = UBITS(253, 5); /* skip 102 bits */ + imo = true; break; case 21: /* IMO289 - Weather obs. report from ship */ break; @@ -519,12 +532,14 @@ bool ais_binary_decode(struct ais_t *ais, } #undef ARRAY_BASE #undef ELEMENT_SIZE + imo = true; break; case 29: /* IMO289 - Text Description - broadcast */ ais->type8.dac1fid29.linkage = UBITS(56, 10); from_sixbit((char *)bits, 66, bitlen-66, ais->type8.dac1fid29.text); + imo = true; break; case 31: /* IMO289 - Meteorological/Hydrological data */ ais->type8.dac1fid31.lon = SBITS(56, 25); @@ -807,8 +822,8 @@ bool ais_binary_decode(struct ais_t *ais, ais->type22.area.sw_lon = SBITS(104, 18); ais->type22.area.sw_lat = SBITS(122, 17); } else { - ais->type22.mmsi.dest1 = SBITS(69, 30); - ais->type22.mmsi.dest2 = SBITS(104, 30); + ais->type22.mmsi.dest1 = UBITS(69, 30); + ais->type22.mmsi.dest2 = UBITS(104, 30); } ais->type22.band_a = UBITS(140, 1); ais->type22.band_b = UBITS(141, 1); @@ -946,6 +961,11 @@ bool ais_binary_decode(struct ais_t *ais, (ais->type26.bitcount + 7) / 8); break; case 27: /* Long Range AIS Broadcast message */ + if (bitlen != 96) { + gpsd_report(LOG_WARN, "AIVDM message type 27 size not 96 bits (%zd).\n", + bitlen); + return false; + } ais->type27.accuracy = (bool)UBITS(38, 1); ais->type27.raim = UBITS(39, 1)!=0; ais->type27.status = UBITS(40, 4); diff --git a/gpsd_json.c b/gpsd_json.c index c5a00849..7fb359ab 100644 --- a/gpsd_json.c +++ b/gpsd_json.c @@ -1369,6 +1369,7 @@ void json_aivdm_dump(const struct ais_t *ais, char buf1[JSON_VAL_MAX * 2 + 1]; char buf2[JSON_VAL_MAX * 2 + 1]; char buf3[JSON_VAL_MAX * 2 + 1]; + char buf4[JSON_VAL_MAX * 2 + 1]; bool imo; int i; @@ -1636,8 +1637,8 @@ void json_aivdm_dump(const struct ais_t *ais, static const char *light_status[] = { "No light or no monitoring", - "Light ON" - "Light OFF" + "Light ON", + "Light OFF", "Light ERROR" }; @@ -1725,8 +1726,10 @@ void json_aivdm_dump(const struct ais_t *ais, case 11: /* UTC/Date Response */ /* some fields have beem merged to an ISO8601 date */ if (scaled) { + // The use of %u instead of %04u for the year is to allow + // out-of-band year values. (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"timestamp\":\"%4u-%02u-%02uT%02u:%02u:%02uZ\"," + "\"timestamp\":\"%u-%02u-%02uT%02u:%02u:%02uZ\"," "\"accuracy\":%s,\"lon\":%.4f,\"lat\":%.4f," "\"epfd\":\"%s\",\"raim\":%s,\"radio\":%u}\r\n", ais->type4.year, @@ -1742,7 +1745,7 @@ void json_aivdm_dump(const struct ais_t *ais, JSON_BOOL(ais->type4.raim), ais->type4.radio); } else { (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"timestamp\":\"%04u-%02u-%02uT%02u:%02u:%02uZ\"," + "\"timestamp\":\"%u-%02u-%02uT%02u:%02u:%02uZ\"," "\"accuracy\":%s,\"lon\":%d,\"lat\":%d," "\"epfd\":%u,\"raim\":%s,\"radio\":%u}\r\n", ais->type4.year, @@ -1882,26 +1885,32 @@ void json_aivdm_dump(const struct ais_t *ais, "\"nextport\":\"%s\",\"eta\":\"%02u-%02uT%02u:%02uZ\"," "\"dangerous\":\"%s\",\"imdcat\":\"%s\"," "\"unid\":%u,\"amount\":%u,\"unit\":%u}\r\n", - ais->type6.dac1fid12.lastport, + json_stringify(buf1, sizeof(buf1), + ais->type6.dac1fid12.lastport), ais->type6.dac1fid12.lmonth, ais->type6.dac1fid12.lday, ais->type6.dac1fid12.lhour, ais->type6.dac1fid12.lminute, - ais->type6.dac1fid12.nextport, + json_stringify(buf2, sizeof(buf2), + ais->type6.dac1fid12.nextport), ais->type6.dac1fid12.nmonth, ais->type6.dac1fid12.nday, ais->type6.dac1fid12.nhour, ais->type6.dac1fid12.nminute, - ais->type6.dac1fid12.dangerous, - ais->type6.dac1fid12.imdcat, + json_stringify(buf3, sizeof(buf3), + ais->type6.dac1fid12.dangerous), + json_stringify(buf4, sizeof(buf4), + ais->type6.dac1fid12.imdcat), ais->type6.dac1fid12.unid, ais->type6.dac1fid12.amount, ais->type6.dac1fid12.unit); + imo = true; break; case 15: /* IMO236 - Extended Ship Static and Voyage Related Data */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"airdraught\":%u}\r\n", ais->type6.dac1fid15.airdraught); + imo = true; break; case 16: /* IMO236 - Number of persons on board */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), @@ -1910,14 +1919,16 @@ void json_aivdm_dump(const struct ais_t *ais, break; case 18: /* IMO289 - Clearance time to enter port */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"linkage\":%u,\"arrival\":\"%u-%uT%u:%uZ\",\"portname\":\"%s\",\"destination\":\"%s\",", + "\"linkage\":%u,\"arrival\":\"%02u-%02uT%02u:%02uZ\",\"portname\":\"%s\",\"destination\":\"%s\",", ais->type6.dac1fid18.linkage, ais->type6.dac1fid18.month, ais->type6.dac1fid18.day, - ais->type6.dac1fid18.hour, + ais->type6.dac1fid18.hour, ais->type6.dac1fid18.minute, - ais->type6.dac1fid18.portname, - ais->type6.dac1fid18.destination); + json_stringify(buf1, sizeof(buf1), + ais->type6.dac1fid18.portname), + json_stringify(buf2, sizeof(buf2), + ais->type6.dac1fid18.destination)); if (scaled) (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"lon\":%.3f,\"lat\":%.3f}\r\n", @@ -1928,6 +1939,7 @@ void json_aivdm_dump(const struct ais_t *ais, "\"lon\":%d,\"lat\":%d}\r\n", ais->type6.dac1fid18.lon, ais->type6.dac1fid18.lat); + imo = true; break; case 20: /* IMO289 - Berthing Data */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), @@ -1942,7 +1954,7 @@ void json_aivdm_dump(const struct ais_t *ais, "\"shiprepair\":%u,\"surveyor\":%u," "\"steam\":%u,\"tugs\":%u,\"solidwaste\":%u," "\"liquidwaste\":%u,\"hazardouswaste\":%u," - "\"ballast\":%u,\"additional\":%u,\"" + "\"ballast\":%u,\"additional\":%u," "\"regional1\":%u,\"regional2\":%u," "\"future1\":%u,\"future2\":%u," "\"berth_name\":\"%s\",", @@ -1980,7 +1992,8 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type6.dac1fid20.regional2, ais->type6.dac1fid20.future1, ais->type6.dac1fid20.future2, - ais->type6.dac1fid20.berth_name); + json_stringify(buf1, sizeof(buf1), + ais->type6.dac1fid20.berth_name)); if (scaled) (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"berth_lon\":%.3f," @@ -1997,6 +2010,7 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type6.dac1fid20.berth_lon, ais->type6.dac1fid20.berth_lat, ais->type6.dac1fid20.berth_depth); + imo = true; break; case 23: /* IMO289 - Area notice - addressed */ break; @@ -2013,7 +2027,8 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type6.dac1fid25.cargos[i].subtype); if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0'; - (void)strlcat(buf, "]}\r\n,", buflen); + (void)strlcat(buf, "]}\r\n", buflen); + imo = true; break; case 28: /* IMO289 - Route info - addressed */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), @@ -2029,7 +2044,7 @@ void json_aivdm_dump(const struct ais_t *ais, "\"rtype\":%u,", ais->type6.dac1fid28.rtype); (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"start\":\"%02u-%02uT%02u:%02uZ\",\"duration\":%u,\"waypoints:[", + "\"start\":\"%02u-%02uT%02u:%02uZ\",\"duration\":%u,\"waypoints\":[", ais->type6.dac1fid28.month, ais->type6.dac1fid28.day, ais->type6.dac1fid28.hour, @@ -2049,14 +2064,16 @@ void json_aivdm_dump(const struct ais_t *ais, } if (buf[strlen(buf) - 1] == ',') buf[strlen(buf)-1] = '\0'; - (void)strlcat(buf, "]}\r\n,", buflen); + (void)strlcat(buf, "]}\r\n", buflen); + imo = true; break; case 30: /* IMO289 - Text description - addressed */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"linkage\":%u,\"text\":\"%s\"}\r\n", ais->type6.dac1fid30.linkage, - json_stringify(buf1, sizeof(buf1), + json_stringify(buf1, sizeof(buf1), ais->type6.dac1fid30.text)); + imo = true; break; case 14: /* IMO236 - Tidal Window */ case 32: /* IMO289 - Tidal Window */ @@ -2077,7 +2094,7 @@ void json_aivdm_dump(const struct ais_t *ais, tp->lon, tp->lat); (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"from_hour\":%u,\"from_min\":%u,\"to_hour\":%u,\"to_min\":%u,\"cdir\":%u", + "\"from_hour\":%u,\"from_min\":%u,\"to_hour\":%u,\"to_min\":%u,\"cdir\":%u,", tp->from_hour, tp->from_min, tp->to_hour, @@ -2094,15 +2111,17 @@ void json_aivdm_dump(const struct ais_t *ais, } if (buf[strlen(buf) - 1] == ',') buf[strlen(buf)-1] = '\0'; - (void)strlcat(buf, "]}\r\n,", buflen); + (void)strlcat(buf, "]}\r\n", buflen); + imo = true; break; } if (!imo) (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"data\":\"%zd:%s\"}\r\n", ais->type6.bitcount, - gpsd_hexdump((char *)ais->type6.bitdata, - (ais->type6.bitcount + 7) / 8)); + json_stringify(buf1, sizeof(buf1), + gpsd_hexdump((char *)ais->type6.bitdata, + (ais->type6.bitcount + 7) / 8))); break; case 7: /* Binary Acknowledge */ case 13: /* Safety Related Acknowledge */ @@ -2269,9 +2288,12 @@ void json_aivdm_dump(const struct ais_t *ais, "\"extunit\":%u," "\"from\":\"%02u-%02uT%02u:%02u\"," "\"to\":\"%02u-%02uT%02u:%02u\"}\r\n", - ais->type8.dac1fid13.reason, - ais->type8.dac1fid13.closefrom, - ais->type8.dac1fid13.closeto, + json_stringify(buf1, sizeof(buf1), + ais->type8.dac1fid13.reason), + json_stringify(buf2, sizeof(buf2), + ais->type8.dac1fid13.closefrom), + json_stringify(buf3, sizeof(buf3), + ais->type8.dac1fid13.closeto), ais->type8.dac1fid13.radius, ais->type8.dac1fid13.extunit, ais->type8.dac1fid13.fmonth, @@ -2282,11 +2304,13 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type8.dac1fid13.tday, ais->type8.dac1fid13.thour, ais->type8.dac1fid13.tminute); + imo = true; break; case 15: /* IMO236 - Extended ship and voyage */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"airdraught\":%u}\r\n", ais->type8.dac1fid15.airdraught); + imo = true; break; case 17: /* IMO289 - VTS-generated/synthetic targets */ (void)strlcat(buf, "\"targets\":[", buflen); @@ -2316,13 +2340,15 @@ void json_aivdm_dump(const struct ais_t *ais, (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"%s\":\"%s\",", idtypes[ais->type8.dac1fid17.targets[i].idtype], - ais->type8.dac1fid17.targets[i].id.callsign); + json_stringify(buf1, sizeof(buf1), + ais->type8.dac1fid17.targets[i].id.callsign)); break; default: (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"%s\":\"%s\",", idtypes[ais->type8.dac1fid17.targets[i].idtype], - ais->type8.dac1fid17.targets[i].id.other); + json_stringify(buf1, sizeof(buf1), + ais->type8.dac1fid17.targets[i].id.other)); } if (scaled) (void)snprintf(buf + strlen(buf), buflen - strlen(buf), @@ -2342,14 +2368,16 @@ void json_aivdm_dump(const struct ais_t *ais, } if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0'; - (void)strlcat(buf, "]}\r\n,", buflen); + (void)strlcat(buf, "]}\r\n", buflen); + imo = true; break; case 19: /* IMO289 - Marine Traffic Signal */ if (scaled) (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"linkage\":%u,\"station\":\"%s\",\"lon\":%.3f,\"lat\":%.3f,\"status\":%u,\"signal\":\"%s\",\"hour\":%u,\"minute\":%u,\"nextsignal\":\"%s\"}\r\n", ais->type8.dac1fid19.linkage, - ais->type8.dac1fid19.station, + json_stringify(buf1, sizeof(buf1), + ais->type8.dac1fid19.station), ais->type8.dac1fid19.lon / AIS_LATLON3_SCALE, ais->type8.dac1fid19.lat / AIS_LATLON3_SCALE, ais->type8.dac1fid19.status, @@ -2361,7 +2389,8 @@ void json_aivdm_dump(const struct ais_t *ais, (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"linkage\":%u,\"station\":\"%s\",\"lon\":%d,\"lat\":%d,\"status\":%u,\"signal\":%u,\"hour\":%u,\"minute\":%u,\"nextsignal\":%u}\r\n", ais->type8.dac1fid19.linkage, - ais->type8.dac1fid19.station, + json_stringify(buf1, sizeof(buf1), + ais->type8.dac1fid19.station), ais->type8.dac1fid19.lon, ais->type8.dac1fid19.lat, ais->type8.dac1fid19.status, @@ -2369,6 +2398,7 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type8.dac1fid19.hour, ais->type8.dac1fid19.minute, ais->type8.dac1fid19.nextsignal); + imo = true; break; case 21: /* IMO289 - Weather obs. report from ship */ break; @@ -2412,14 +2442,16 @@ void json_aivdm_dump(const struct ais_t *ais, } if (buf[strlen(buf) - 1] == ',') buf[strlen(buf) - 1] = '\0'; - (void)strlcat(buf, "]}\r\n,", buflen); + (void)strlcat(buf, "]}\r\n", buflen); + imo = true; break; case 29: /* IMO289 - Text Description - broadcast */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"linkage\":%u,\"text\":\"%s\"}\r\n", ais->type8.dac1fid29.linkage, - json_stringify(buf1, sizeof(buf1), + json_stringify(buf1, sizeof(buf1), ais->type8.dac1fid29.text)); + imo = true; break; case 31: /* IMO289 - Meteorological/Hydrological data */ /* some fields have been merged to an ISO8601 partial date */ @@ -2556,8 +2588,9 @@ void json_aivdm_dump(const struct ais_t *ais, (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"data\":\"%zd:%s\"}\r\n", ais->type8.bitcount, - gpsd_hexdump((char *)ais->type8.bitdata, - (ais->type8.bitcount + 7) / 8)); + json_stringify(buf1, sizeof(buf1), + gpsd_hexdump((char *)ais->type8.bitdata, + (ais->type8.bitcount + 7) / 8))); break; case 9: /* Standard SAR Aircraft Position Report */ if (scaled) { @@ -2746,7 +2779,8 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type19.heading, ais->type19.second, ais->type19.regional, - ais->type19.shipname, + json_stringify(buf1, sizeof(buf1), + ais->type19.shipname), SHIPTYPE_DISPLAY(ais->type19.shiptype), ais->type19.to_bow, ais->type19.to_stern, @@ -2773,7 +2807,8 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type19.heading, ais->type19.second, ais->type19.regional, - ais->type19.shipname, + json_stringify(buf1, sizeof(buf1), + ais->type19.shipname), ais->type19.shiptype, ais->type19.to_bow, ais->type19.to_stern, @@ -2842,7 +2877,8 @@ void json_aivdm_dump(const struct ais_t *ais, "\"off_position\":%s,\"raim\":%s," "\"virtual_aid\":%s}\r\n", ais->type21.aid_type, - ais->type21.name, + json_stringify(buf1, sizeof(buf1), + ais->type21.name), JSON_BOOL(ais->type21.accuracy), ais->type21.lon, ais->type21.lat, @@ -2867,7 +2903,7 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type22.txrx, JSON_BOOL(ais->type22.power)); if (ais->type22.addressed) { (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"dest1\":%u,\"dest2\":%u", + "\"dest1\":%u,\"dest2\":%u,", ais->type22.mmsi.dest1, ais->type22.mmsi.dest2); } else if (scaled) { (void)snprintf(buf + strlen(buf), buflen - strlen(buf), @@ -2937,7 +2973,10 @@ void json_aivdm_dump(const struct ais_t *ais, } (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"vendorid\":\"%s\",\"callsign\":\"%s\",", - ais->type24.vendorid, ais->type24.callsign); + json_stringify(buf1, sizeof(buf1), + ais->type24.vendorid), + json_stringify(buf2, sizeof(buf2), + ais->type24.callsign)); if (AIS_AUXILIARY_MMSI(ais->mmsi)) { (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "mothership_\"mmsi\":%u}\r\n", @@ -2967,7 +3006,7 @@ void json_aivdm_dump(const struct ais_t *ais, case 26: /* Binary Message, Multiple Slot */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), "\"addressed\":%s,\"structured\":%s,\"dest_mmsi\":%u," - "\"app_id\":%u,\"data\":\"%zd:%s\"\"radio\":%u}\r\n", + "\"app_id\":%u,\"data\":\"%zd:%s\",\"radio\":%u}\r\n", JSON_BOOL(ais->type26.addressed), JSON_BOOL(ais->type26.structured), ais->type26.dest_mmsi, @@ -2978,18 +3017,32 @@ void json_aivdm_dump(const struct ais_t *ais, ais->type26.radio); break; case 27: /* Long Range AIS Broadcast message */ - (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "\"status\":\"%s\"," - "\"accuracy\":%s,\"lon\":%.1f,\"lat\":%.1f," - "\"speed\":%u,\"course\":%u,raim\":%s,\"gnss\":%s}\r\n", - nav_legends[ais->type27.status], - JSON_BOOL(ais->type27.accuracy), - ais->type27.lon / AIS_LONGRANGE_LATLON_SCALE, - ais->type27.lat / AIS_LONGRANGE_LATLON_SCALE, - ais->type27.speed, - ais->type27.course, - JSON_BOOL(ais->type27.raim), - JSON_BOOL(ais->type27.gnss)); + if (scaled) + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"status\":\"%s\"," + "\"accuracy\":%s,\"lon\":%.1f,\"lat\":%.1f," + "\"speed\":%u,\"course\":%u,\"raim\":%s,\"gnss\":%s}\r\n", + nav_legends[ais->type27.status], + JSON_BOOL(ais->type27.accuracy), + ais->type27.lon / AIS_LONGRANGE_LATLON_SCALE, + ais->type27.lat / AIS_LONGRANGE_LATLON_SCALE, + ais->type27.speed, + ais->type27.course, + JSON_BOOL(ais->type27.raim), + JSON_BOOL(ais->type27.gnss)); + else + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"status\":%u," + "\"accuracy\":%s,\"lon\":%d,\"lat\":%d," + "\"speed\":%u,\"course\":%u,\"raim\":%s,\"gnss\":%s}\r\n", + ais->type27.status, + JSON_BOOL(ais->type27.accuracy), + ais->type27.lon, + ais->type27.lat, + ais->type27.speed, + ais->type27.course, + JSON_BOOL(ais->type27.raim), + JSON_BOOL(ais->type27.gnss)); break; default: if (buf[strlen(buf) - 1] == ',') diff --git a/gpsdecode.c b/gpsdecode.c index 51a9c2eb..1664e5ac 100644 --- a/gpsdecode.c +++ b/gpsdecode.c @@ -443,7 +443,7 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen) break; case 25: /* Binary Message, Single Slot */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "%u|%u|%u|%u|%zd:%s\r\n", + "%u|%u|%u|%u|%zd:%s", (uint) ais->type25.addressed, (uint) ais->type25.structured, ais->type25.dest_mmsi, @@ -454,7 +454,7 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen) break; case 26: /* Binary Message, Multiple Slot */ (void)snprintf(buf + strlen(buf), buflen - strlen(buf), - "%u|%u|%u|%u|%zd:%s:%u\r\n", + "%u|%u|%u|%u|%zd:%s:%u", (uint) ais->type26.addressed, (uint) ais->type26.structured, ais->type26.dest_mmsi, @@ -464,6 +464,18 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen) (ais->type26.bitcount + 7) / 8), ais->type26.radio); break; + case 27: /* Long Range AIS Broadcast message */ + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "%u|%u|%d|%d|%u|%u|%u|%u", + ais->type27.status, + (uint)ais->type27.accuracy, + ais->type27.lon, + ais->type27.lat, + ais->type27.speed, + ais->type27.course, + (uint)ais->type27.raim, + (uint)ais->type27.gnss); + break; default: (void)snprintf(buf + strlen(buf), buflen - strlen(buf), diff --git a/jsongen.py.in b/jsongen.py.in index cf785291..227a8f38 100644 --- a/jsongen.py.in +++ b/jsongen.py.in @@ -808,7 +808,10 @@ def generate(spec): else: print >>sys.stderr, "explicit length specification required" raise SystemExit, 1 - report += " };\n" + report += """\ + {NULL} + }; +""" # Generate the main structure definition describing this parse. # It may have object subarrays. if pacify_splint: diff --git a/test/sample.aivdm b/test/sample.aivdm index a12dc169..7637fe0c 100644 --- a/test/sample.aivdm +++ b/test/sample.aivdm @@ -201,6 +201,14 @@ # # FIX-ME: We need a type 6 test case that requires more than one AIVDM fragment. # +# Type 6: +# Advertised as DAC 1, FID 12, but full of garbage. +!AIVDM,1,1,,A,63LBA4;WBevJ04k0=@E=B0td,0*17 +# Advertised as DAC 1, FID 14, but full of garbage +!AIVDM,1,1,,A,6h2E:81>NmKC04p0J<000?vv20Ru,0*31 +# Advertised as DAC 1, FID 18, but full of 0 +!AIVDM,1,1,,B,6h2E3MDrDRiB0580@00000000000,0*04 + # Type 7: # From AISHub - reported immediately after the preceding type 6, # which matches it. One destination MMSI. noaadata-0.43 fails @@ -591,7 +599,10 @@ # Message type : 0 # Slot offset : 0 # -# FIX-ME: Need an example of the 160-bit variant of type 15 with two MMSIs. +# Type 15: +# From Christian Gagneraud via aishub +# This is the 160-bit variant of type 15 with two MMSIs. +!AIVDM,1,1,,A,?39a?2PjKFFPD01o:Gq1igvp2<3w,0*0B # # Type 16: # From AISHub. These are only a regression test to check that the C and Python @@ -792,6 +803,11 @@ DGNSS data : 376:7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062 # Channel A Band : 0 # Zone size : 4 # +# Type 22: +# From Christian Gagneraud via aishub +# Addressed case. +!AIVDM,1,1,,A,F@@W>gOP00PH=JrN9l000?wB2HH;,0*44 +# # Type 23: # From AISHub. Only a regression test to check that the C and Python decoders # do the same thing, not yet checked against other decoders. @@ -831,6 +847,51 @@ DGNSS data : 376:7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062 # dimC: 0 # dimD: 5 # +# Type 25: +# From Christian Gagneraud via aishub +# Addressed and structured +!AIVDM,1,1,,A,JB3R0GO7p>vQL8tjw0b5hqpd0706kh9d3lR2vbl0400,2*40 +# +# Type 25: +# From Christian Gagneraud via aishub +# Addressed and not structured +!AIVDM,1,1,,A,I6SWo?8P00a3PKpEKEVj0?vNP<65,0*73 +# +# Type 25: +# From Christian Gagneraud via aishub +# Broadcast and structured +!AIVDM,1,1,,A,I8IRGB40QPPa0:<HP::V=gwv0l48,0*0E +# +# Type 25: +# From Christian Gagneraud via aishub +# Broadcast and not structured +!AIVDM,1,1,,A,I6SWVNP001a3P8FEKNf=Qb0@00S8,0*6B +# +# Type 26: +# From Christian Gagneraud via aishub +# Addressed and structured +!AIVDM,1,1,,A,JB3R0GO7p>vQL8tjw0b5hqpd0706kh9d3lR2vbl0400,2*40 +# +# Type 26: +# From Christian Gagneraud via aishub +# Addressed and not structured +!AIVDM,1,1,,A,J1@@0IK70PGgT740000000000@000?D0ih1e00006JlPC9C3,0*6B +# +# Type 26 +# From Christian Gagneraud via aishub +# Broadcast and structured +!AIVDM,1,1,,B,JaL0mr5P000DtRDMddr@0?vF06iD,0*75 +# +# Type 26 +# From Christian Gagneraud via aishub +# Broadcast and not structured +!AIVDM,1,1,,A,J0@00@370>t0Lh3P0000200H:2rN92,4*14 +# +# Type 27: +# From Christian Gagneraud via aishub +# The only message 27 in more than 25 millions messages that is 96 bits long! +!AIVDM,1,1,,A,KCQ9r=hrFUnH7P00,0*41 +# # ############################################################################## # Invalid packet tests: @@ -874,3 +935,28 @@ AIVDM,2,2,1,B,00000000000,2*26 !AIVDM,2,1,2,A,542M92h00001@<7;?G0PD4i@R0<tqA8tj37>220o0h:2240Ht500000000000000,0*3C !AIVDM,2,2,2,A,0000002,2*24 !AIVDM,2,2,6,B,00000000000,2*21 +############################################################################## +# Error and corner case tests: +############################################################################## +# Non printable and control character in data or string fields +!AIVDM,1,1,,A,647sBv00b=E006P9>0,4*1B +# Type 4 with date/time set to N/A +!AIVDM,1,1,,A,402Fha0000Htt<tSF0l4Q@000d20,0*65 +# Type 4 with YYYY/MM/DD OK, but HHmmSS N/A +!AIVDM,1,1,,B,4028n@iuiPpttwIWI<Hl>8700PS:,0*60 +# Type 4 with year set to 10196 (0x27D4), idempotency use to fail on this one +# It has a bad epfd as well. +!AIVDM,1,1,,B,4>O7m7Iu@<9qUfbtm`vSnwvH20S8,0*46 +# Type 22 with MMSI1=4059193694, use to fail due to UBITS() instead of SBITS() +!AIVDM,1,1,,B,Fe3>>MOD@GDF?ThcoCk02?ioQie4,0*03 +# A type 6, DAC 1, FID 18 with plenty of N/A fields, idempotency use to fail +!AIVDM,1,1,,B,602E:s0tw@9B0580@00000000000,0*68 +# A type 6, DAC 1, FID 30. use to fail idempotency +!AIVDM,1,1,,B,6h2E3MPr<buN05p0J00000000000,0*0A +# A type 6, DAC 1, FID 18, use to fail idempotency +!AIVDM,1,1,,B,602E:s0tw@9B0580@00000000000,0*68 +# Array of struct. use to crash "gpsdecode -j -e -u" +!AIVDM,1,1,,A,6h2E3MHrg19P0600@00000000000,0*02 +!AIVDM,1,1,,A,6h2E3N0rThqP0600J00000000000,0*51 + + diff --git a/test/sample.aivdm.chk b/test/sample.aivdm.chk index 3b698060..31dc496e 100644 --- a/test/sample.aivdm.chk +++ b/test/sample.aivdm.chk @@ -7,6 +7,9 @@ 6|1|150834090|3|313240222|0|669|11|48:eb2f118f7ff1
6|0|992509976|0|2500912|0|235|10|274|1|1|2|2|0|0|0
6|0|265538450|0|2655651|0|1|40|16:0000
+6|0|230986000|2|970110950|1|1|12|56:30435445530000
+6|3|002443808|0|329176500|1|1|14|80:00000000000000000000
+6|3|002442101|1|244615956|1|1|18|80:01000000000000000000
7|0|002655651|265538450|0|0|0
7|1|655901842|158483613|321823389|836359488|0
7|2|537411077|43101326|717096664|76161024|0
@@ -32,6 +35,7 @@ 14|0|311764000|TEST
15|0|368578000|5158|5|0|0|0|0|0|0
15|3|003669720|367014320|3|516|5|617|0|0|0
+15|0|211439370|211507560|5|0|55|663|605843451|32|560
16|0|002053501|224251000|200|0|0|0|0
17|0|002734450|17478|35992|376:7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062922fe3809292afde9122929fcf7002923ffd20c29aaaa
18|0|338087471|0|1|0|-44443279|24410724|796|511|49|0x0|1|0|1|1|1|1|0xe0006
@@ -42,8 +46,28 @@ 20|0|003160097|47|1|7|250|2250|1|7|1125|856|5|7|1125|0|0|0|0
21|0|123456789|20|CHINA ROSE MURPHY EXPRESS ALERT|0|-73619155|28752371|5|5|5|5|1|50|165|0x0|0|0
22|0|003160048|2087|2088|0|0|-44100|27330|-48100|25400|0|0|0|4
+22|1|017419965|3584|8|1|1|28144881|268435519|1|0|0|4
23|0|002268120|1578|30642|1096|30408|6|0|2|9|0
24|0|271041815|PROGUY|60|1D00014|TC6163|0|15|0|5
+26|1|137920605|1|1|838351848|23587|180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00:0
+25|0|440006460|1|0|134218384|0|128:20000a4381be156d59b200ff9e80c185
+25|0|563648328|0|1|0|134|112:082900a31880a2a636fffe034108
+25|0|440002170|0|0|0|0|128:00001a438085956deb8d86a0100008c8
+26|1|137920605|1|1|838351848|23587|180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00:0
+26|0|084148325|1|0|834699643|0|228:q:0
+26|2|633353704|0|1|0|24576|92:0014f2251db2ce9000ff9600:0
+26|0|016777280|0|0|0|0|116::0
+27|1|236091959|3|0|-92521|52239|0|0|0|0
24|0|271040660|GOZDEM-1|37|1C00045|YM5504|0|24|0|6
5|0|271010059|0|0|TCA2350|HEALTH CONTROL 13|55|6|10|2|2|1|00-00T24:60Z|20||0
5|0|271010059|0|0|TCA2350|HEALTH CONTROL 13|55|6|10|2|2|1|00-00T24:60Z|20||0
+6|0|276747000|0|2766160|0|1|40|16: 8
+4|0|002470052|0000-00-00T24:60:60Z|0|108600000|54600000|0|0|0x2c080
+4|0|002242115|2012-06-01T24:60:60Z|1|-5031130|26021408|7|0|0x208ca
+4|0|972158237|10196-00-24T09:57:37Z|1|123070132|65599231|14|1|0x8c8
+22|2|875794037|3396|373|1|0|837968222|254804543|1|0|1|7
+6|0|002444012|0|255803540|1|1|18|80:01000000000000000000
+6|3|002442102|0|244100055|1|1|30|80:01000000280000000000
+6|0|002444012|0|255803540|1|1|18|80:01000000000000000000
+6|3|002442101|2|246351000|0|1|32|80:00000000000000000000
+6|3|002442104|0|245679000|0|1|32|80:00000000000000000000
diff --git a/test/sample.aivdm.js.chk b/test/sample.aivdm.js.chk index edaf887b..ff61c1b5 100644 --- a/test/sample.aivdm.js.chk +++ b/test/sample.aivdm.js.chk @@ -5,8 +5,11 @@ {"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":3669702,"scaled":true,"timestamp":"2007-05-14T19:57:39Z","accuracy":true,"lon":-76.3524,"lat":36.8838,"epfd":"Surveyed","raim":false,"radio":67039}
{"class":"AIS","device":"stdin","type":5,"repeat":0,"mmsi":351759000,"scaled":true,"imo":9134270,"ais_version":0,"callsign":"3FOF8","shipname":"EVER DIADEM","shiptype":"Cargo - all ships of this type","to_bow":225,"to_stern":70,"to_port":1,"to_starboard":31,"epfd":"GPS","eta":"05-15T14:00Z","draught":12.2,"destination":"NEW YORK","dte":0}
{"class":"AIS","device":"stdin","type":6,"repeat":1,"mmsi":150834090,"scaled":true,"seqno":3,"dest_mmsi":313240222,"retransmit":false,"dac":669,"fid":11,"data":"48:eb2f118f7ff1"}
-{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":992509976,"scaled":true,"seqno":0,"dest_mmsi":2500912,"retransmit":false,"dac":235,"fid":10,"off_pos":false,"alarm":false,"stat_ext":0,"ana_int":13.70,"ana_ext1":0.05,"ana_ext2":0.05,"racon":"RACON operational","light":"No RACON installed"}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":992509976,"scaled":true,"seqno":0,"dest_mmsi":2500912,"retransmit":false,"dac":235,"fid":10,"off_pos":false,"alarm":false,"stat_ext":0,"ana_int":13.70,"ana_ext1":0.05,"ana_ext2":0.05,"racon":"RACON operational","light":"Light OFF"}
{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":265538450,"scaled":true,"seqno":0,"dest_mmsi":2655651,"retransmit":false,"dac":1,"fid":40,"data":"16:0000"}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":230986000,"scaled":true,"seqno":2,"dest_mmsi":970110950,"retransmit":true,"dac":1,"fid":12,"lastport":"0CTES","departure":"05-04T00:60Z","nextport":",","eta":"00-00T00:00Z","dangerous":"","imdcat":"","unid":0,"amount":0,"unit":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2443808,"scaled":true,"seqno":0,"dest_mmsi":329176500,"retransmit":true,"dac":1,"fid":14,"month":0,"day":0,"tidals":[{"lon":279.339,"lat":-406.323,"from_hour":1,"from_min":5,"to_hour":29,"to_min":0,"cdir":0,"cspeed":0.0}]}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442101,"scaled":true,"seqno":1,"dest_mmsi":244615956,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0.000,"lat":0.000}
{"class":"AIS","device":"stdin","type":7,"repeat":0,"mmsi":2655651,"scaled":true,"mmsi1":265538450,"mmsi2":0,"mmsi3":0,"mmsi4":0}
{"class":"AIS","device":"stdin","type":7,"repeat":1,"mmsi":655901842,"scaled":true,"mmsi1":158483613,"mmsi2":321823389,"mmsi3":836359488,"mmsi4":0}
{"class":"AIS","device":"stdin","type":7,"repeat":2,"mmsi":537411077,"scaled":true,"mmsi1":43101326,"mmsi2":717096664,"mmsi3":76161024,"mmsi4":0}
@@ -32,6 +35,7 @@ {"class":"AIS","device":"stdin","type":14,"repeat":0,"mmsi":311764000,"scaled":true,"text":"TEST"}
{"class":"AIS","device":"stdin","type":15,"repeat":0,"mmsi":368578000,"scaled":true,"mmsi1":5158,"type1_1":5,"offset1_1":0,"type1_2":0,"offset1_2":0,"mmsi2":0,"type2_1":0,"offset2_1":0}
{"class":"AIS","device":"stdin","type":15,"repeat":3,"mmsi":3669720,"scaled":true,"mmsi1":367014320,"type1_1":3,"offset1_1":516,"type1_2":5,"offset1_2":617,"mmsi2":0,"type2_1":0,"offset2_1":0}
+{"class":"AIS","device":"stdin","type":15,"repeat":0,"mmsi":211439370,"scaled":true,"mmsi1":211507560,"type1_1":5,"offset1_1":0,"type1_2":55,"offset1_2":663,"mmsi2":605843451,"type2_1":32,"offset2_1":560}
{"class":"AIS","device":"stdin","type":16,"repeat":0,"mmsi":2053501,"scaled":true,"mmsi1":224251000,"offset1":200,"increment1":0,"mmsi2":0,"offset2":0,"increment2":0}
{"class":"AIS","device":"stdin","type":17,"repeat":0,"mmsi":2734450,"scaled":true,"lon":29.1,"lat":60.0,"data":"376:7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062922fe3809292afde9122929fcf7002923ffd20c29aaaa"}
{"class":"AIS","device":"stdin","type":18,"repeat":0,"mmsi":338087471,"scaled":true,"reserved":0,"speed":0.1,"accuracy":false,"lon":-74.0721,"lat":40.6845,"course":79.6,"heading":511,"second":49,"regional":0,"cs":true,"display":false,"dsc":true,"band":true,"msg22":true,"raim":true,"radio":917510}
@@ -42,8 +46,28 @@ {"class":"AIS","device":"stdin","type":20,"repeat":0,"mmsi":3160097,"scaled":true,"offset1":47,"number1":1,"timeout1":7,"increment1":250,"offset2":2250,"number2":1,"timeout2":7,"increment2":1125,"offset3":856,"number3":5,"timeout3":7,"increment3":1125,"offset4":0,"number4":0,"timeout4":0,"increment4":0}
{"class":"AIS","device":"stdin","type":21,"repeat":0,"mmsi":123456789,"scaled":true,"aid_type":"INVALID NAVAID TYPE","name":"CHINA ROSE MURPHY EXPRESS ALERT","lon":-122.6986,"lat":47.9206,"accuracy":false,"to_bow":5,"to_stern":5,"to_port":5,"to_starboard":5,"epfd":"GPS","second":50,"regional":165,"off_position":false,"raim":false,"virtual_aid":false}
{"class":"AIS","device":"stdin","type":22,"repeat":0,"mmsi":3160048,"scaled":true,"channel_a":2087,"channel_b":2088,"txrx":0,"power":false,"ne_lon":"-73.500000","ne_lat":"45.550000","sw_lon":"-80.166667","sw_lat":"42.333333","addressed":false,"band_a":false,"band_b":false,"zonesize":4}
+{"class":"AIS","device":"stdin","type":22,"repeat":1,"mmsi":17419965,"scaled":true,"channel_a":3584,"channel_b":8,"txrx":1,"power":true,"dest1":28144881,"dest2":268435519,"addressed":true,"band_a":false,"band_b":false,"zonesize":4}
{"class":"AIS","device":"stdin","type":23,"repeat":0,"mmsi":2268120,"scaled":true,"ne_lon":"2.630000","ne_lat":"51.070000","sw_lon":"1.826667","sw_lat":"50.680000","stationtype":"Regional use and inland waterways","shiptype":"Not available","interval":9,"quiet":0}
{"class":"AIS","device":"stdin","type":24,"repeat":0,"mmsi":271041815,"scaled":true,"shipname":"PROGUY","shiptype":"Passenger - all ships of this type","vendorid":"1D00014","callsign":"TC6163","to_bow":0,"to_stern":15,"to_port":0,"to_starboard":5}
+{"class":"AIS","device":"stdin","type":26,"repeat":1,"mmsi":137920605,"scaled":true,"addressed":true,"structured":true,"dest_mmsi":838351848,"app_id":23587,"data":"180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00","radio":0}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":440006460,"scaled":true,"addressed":true,"structured":false,"dest_mmsi":134218384,"app_id":0,"data":"128:20000a4381be156d59b200ff9e80c185"}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":563648328,"scaled":true,"addressed":false,"structured":true,"dest_mmsi":0,"app_id":134,"data":"112:082900a31880a2a636fffe034108"}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":440002170,"scaled":true,"addressed":false,"structured":false,"dest_mmsi":0,"app_id":0,"data":"128:00001a438085956deb8d86a0100008c8"}
+{"class":"AIS","device":"stdin","type":26,"repeat":1,"mmsi":137920605,"scaled":true,"addressed":true,"structured":true,"dest_mmsi":838351848,"app_id":23587,"data":"180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":0,"mmsi":84148325,"scaled":true,"addressed":true,"structured":false,"dest_mmsi":834699643,"app_id":0,"data":"228:q","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":2,"mmsi":633353704,"scaled":true,"addressed":false,"structured":true,"dest_mmsi":0,"app_id":24576,"data":"92:0014f2251db2ce9000ff9600","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":0,"mmsi":16777280,"scaled":true,"addressed":false,"structured":false,"dest_mmsi":0,"app_id":0,"data":"116:","radio":0}
+{"class":"AIS","device":"stdin","type":27,"repeat":1,"mmsi":236091959,"scaled":true,"status":"Restricted manoeuverability","accuracy":false,"lon":-154.2,"lat":87.1,"speed":0,"course":0,"raim":false,"gnss":false}
{"class":"AIS","device":"stdin","type":24,"repeat":0,"mmsi":271040660,"scaled":true,"shipname":"GOZDEM-1","shiptype":"Pleasure Craft","vendorid":"1C00045","callsign":"YM5504","to_bow":0,"to_stern":24,"to_port":0,"to_starboard":6}
{"class":"AIS","device":"stdin","type":5,"repeat":0,"mmsi":271010059,"scaled":true,"imo":0,"ais_version":0,"callsign":"TCA2350","shipname":"HEALTH CONTROL 13","shiptype":"Law Enforcement","to_bow":6,"to_stern":10,"to_port":2,"to_starboard":2,"epfd":"GPS","eta":"00-00T24:60Z","draught":2.0,"destination":"","dte":0}
{"class":"AIS","device":"stdin","type":5,"repeat":0,"mmsi":271010059,"scaled":true,"imo":0,"ais_version":0,"callsign":"TCA2350","shipname":"HEALTH CONTROL 13","shiptype":"Law Enforcement","to_bow":6,"to_stern":10,"to_port":2,"to_starboard":2,"epfd":"GPS","eta":"00-00T24:60Z","draught":2.0,"destination":"","dte":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":276747000,"scaled":true,"seqno":0,"dest_mmsi":2766160,"retransmit":false,"dac":1,"fid":40,"data":"16:\t8"}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":2470052,"scaled":true,"timestamp":"0-00-00T24:60:60Z","accuracy":false,"lon":181.0000,"lat":91.0000,"epfd":"Undefined","raim":false,"radio":180352}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":2242115,"scaled":true,"timestamp":"2012-06-01T24:60:60Z","accuracy":true,"lon":-8.3852,"lat":43.3690,"epfd":"Surveyed","raim":false,"radio":133322}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":972158237,"scaled":true,"timestamp":"10196-00-24T09:57:37Z","accuracy":true,"lon":205.1169,"lat":109.3321,"epfd":"(null)","raim":true,"radio":2248}
+{"class":"AIS","device":"stdin","type":22,"repeat":2,"mmsi":875794037,"scaled":true,"channel_a":3396,"channel_b":373,"txrx":1,"power":false,"dest1":837968222,"dest2":254804543,"addressed":true,"band_a":false,"band_b":true,"zonesize":7}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":2444012,"scaled":true,"seqno":0,"dest_mmsi":255803540,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0.000,"lat":0.000}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442102,"scaled":true,"seqno":0,"dest_mmsi":244100055,"retransmit":true,"dac":1,"fid":30,"linkage":1,"text":"("}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":2444012,"scaled":true,"seqno":0,"dest_mmsi":255803540,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0.000,"lat":0.000}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442101,"scaled":true,"seqno":2,"dest_mmsi":246351000,"retransmit":false,"dac":1,"fid":32,"month":0,"day":0,"tidals":[{"lon":-279.620,"lat":0.000,"from_hour":0,"from_min":0,"to_hour":0,"to_min":0,"cdir":0,"cspeed":0.0}]}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442104,"scaled":true,"seqno":0,"dest_mmsi":245679000,"retransmit":false,"dac":1,"fid":32,"month":0,"day":0,"tidals":[{"lon":-104.858,"lat":0.000,"from_hour":0,"from_min":0,"to_hour":0,"to_min":0,"cdir":0,"cspeed":0.0}]}
diff --git a/test/sample.aivdm.ju.chk b/test/sample.aivdm.ju.chk index db3b3f8c..e73afb80 100644 --- a/test/sample.aivdm.ju.chk +++ b/test/sample.aivdm.ju.chk @@ -7,6 +7,9 @@ {"class":"AIS","device":"stdin","type":6,"repeat":1,"mmsi":150834090,"scaled":false,"seqno":3,"dest_mmsi":313240222,"retransmit":false,"dac":669,"fid":11,"data":"48:eb2f118f7ff1"}
{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":992509976,"scaled":false,"seqno":0,"dest_mmsi":2500912,"retransmit":false,"dac":235,"fid":10,"off_pos":false,"alarm":false,"stat_ext":0,"ana_int":274,"ana_ext1":1,"ana_ext2":1,"racon":2,"light":2}
{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":265538450,"scaled":false,"seqno":0,"dest_mmsi":2655651,"retransmit":false,"dac":1,"fid":40,"data":"16:0000"}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":230986000,"scaled":false,"seqno":2,"dest_mmsi":970110950,"retransmit":true,"dac":1,"fid":12,"lastport":"0CTES","departure":"05-04T00:60Z","nextport":",","eta":"00-00T00:00Z","dangerous":"","imdcat":"","unid":0,"amount":0,"unit":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2443808,"scaled":false,"seqno":0,"dest_mmsi":329176500,"retransmit":true,"dac":1,"fid":14,"month":0,"day":0,"tidals":[{"lon":16760328,"lat":-24379392,"from_hour":1,"from_min":5,"to_hour":29,"to_min":0,"cdir":0,"cspeed":0}]}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442101,"scaled":false,"seqno":1,"dest_mmsi":244615956,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0,"lat":0}
{"class":"AIS","device":"stdin","type":7,"repeat":0,"mmsi":2655651,"scaled":false,"mmsi1":265538450,"mmsi2":0,"mmsi3":0,"mmsi4":0}
{"class":"AIS","device":"stdin","type":7,"repeat":1,"mmsi":655901842,"scaled":false,"mmsi1":158483613,"mmsi2":321823389,"mmsi3":836359488,"mmsi4":0}
{"class":"AIS","device":"stdin","type":7,"repeat":2,"mmsi":537411077,"scaled":false,"mmsi1":43101326,"mmsi2":717096664,"mmsi3":76161024,"mmsi4":0}
@@ -32,6 +35,7 @@ {"class":"AIS","device":"stdin","type":14,"repeat":0,"mmsi":311764000,"scaled":false,"text":"TEST"}
{"class":"AIS","device":"stdin","type":15,"repeat":0,"mmsi":368578000,"scaled":false,"mmsi1":5158,"type1_1":5,"offset1_1":0,"type1_2":0,"offset1_2":0,"mmsi2":0,"type2_1":0,"offset2_1":0}
{"class":"AIS","device":"stdin","type":15,"repeat":3,"mmsi":3669720,"scaled":false,"mmsi1":367014320,"type1_1":3,"offset1_1":516,"type1_2":5,"offset1_2":617,"mmsi2":0,"type2_1":0,"offset2_1":0}
+{"class":"AIS","device":"stdin","type":15,"repeat":0,"mmsi":211439370,"scaled":false,"mmsi1":211507560,"type1_1":5,"offset1_1":0,"type1_2":55,"offset1_2":663,"mmsi2":605843451,"type2_1":32,"offset2_1":560}
{"class":"AIS","device":"stdin","type":16,"repeat":0,"mmsi":2053501,"scaled":false,"mmsi1":224251000,"offset1":200,"increment1":0,"mmsi2":0,"offset2":0,"increment2":0}
{"class":"AIS","device":"stdin","type":17,"repeat":0,"mmsi":2734450,"scaled":false,"lon":17478,"lat":35992,"data":"376:7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062922fe3809292afde9122929fcf7002923ffd20c29aaaa"}
{"class":"AIS","device":"stdin","type":18,"repeat":0,"mmsi":338087471,"scaled":false,"reserved":0,"speed":1,"accuracy":false,"lon":-44443279,"lat":24410724,"course":796,"heading":511,"second":49,"regional":0,"cs":true,"display":false,"dsc":true,"band":true,"msg22":true,"raim":true,"radio":917510}
@@ -42,8 +46,28 @@ {"class":"AIS","device":"stdin","type":20,"repeat":0,"mmsi":3160097,"scaled":false,"offset1":47,"number1":1,"timeout1":7,"increment1":250,"offset2":2250,"number2":1,"timeout2":7,"increment2":1125,"offset3":856,"number3":5,"timeout3":7,"increment3":1125,"offset4":0,"number4":0,"timeout4":0,"increment4":0}
{"class":"AIS","device":"stdin","type":21,"repeat":0,"mmsi":123456789,"scaled":false,"aid_type":20,"name":"CHINA ROSE MURPHY EXPRESS ALERT","accuracy":false,"lon":-73619155,"lat":28752371,"to_bow":5,"to_stern":5,"to_port":5,"to_starboard":5,"epfd":1,"second":50,"regional":165,"off_position":false,"raim":false,"virtual_aid":false}
{"class":"AIS","device":"stdin","type":22,"repeat":0,"mmsi":3160048,"scaled":false,"channel_a":2087,"channel_b":2088,"txrx":0,"power":false,"ne_lon":-44100,"ne_lat":27330,"sw_lon":-48100,"sw_lat":25400,"addressed":false,"band_a":false,"band_b":false,"zonesize":4}
+{"class":"AIS","device":"stdin","type":22,"repeat":1,"mmsi":17419965,"scaled":false,"channel_a":3584,"channel_b":8,"txrx":1,"power":true,"dest1":28144881,"dest2":268435519,"addressed":true,"band_a":false,"band_b":false,"zonesize":4}
{"class":"AIS","device":"stdin","type":23,"repeat":0,"mmsi":2268120,"scaled":false,"ne_lon":1578,"ne_lat":30642,"sw_lon":1096,"sw_lat":30408,"stationtype":6,"shiptype":0,"interval":9,"quiet":0}
{"class":"AIS","device":"stdin","type":24,"repeat":0,"mmsi":271041815,"scaled":false,"shipname":"PROGUY","shiptype":60,"vendorid":"1D00014","callsign":"TC6163","to_bow":0,"to_stern":15,"to_port":0,"to_starboard":5}
+{"class":"AIS","device":"stdin","type":26,"repeat":1,"mmsi":137920605,"scaled":false,"addressed":true,"structured":true,"dest_mmsi":838351848,"app_id":23587,"data":"180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00","radio":0}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":440006460,"scaled":false,"addressed":true,"structured":false,"dest_mmsi":134218384,"app_id":0,"data":"128:20000a4381be156d59b200ff9e80c185"}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":563648328,"scaled":false,"addressed":false,"structured":true,"dest_mmsi":0,"app_id":134,"data":"112:082900a31880a2a636fffe034108"}
+{"class":"AIS","device":"stdin","type":25,"repeat":0,"mmsi":440002170,"scaled":false,"addressed":false,"structured":false,"dest_mmsi":0,"app_id":0,"data":"128:00001a438085956deb8d86a0100008c8"}
+{"class":"AIS","device":"stdin","type":26,"repeat":1,"mmsi":137920605,"scaled":false,"addressed":true,"structured":true,"dest_mmsi":838351848,"app_id":23587,"data":"180:efa1708f32fc0a85c39e2c007006cf026c0f4882faad00","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":0,"mmsi":84148325,"scaled":false,"addressed":true,"structured":false,"dest_mmsi":834699643,"app_id":0,"data":"228:q","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":2,"mmsi":633353704,"scaled":false,"addressed":false,"structured":true,"dest_mmsi":0,"app_id":24576,"data":"92:0014f2251db2ce9000ff9600","radio":0}
+{"class":"AIS","device":"stdin","type":26,"repeat":0,"mmsi":16777280,"scaled":false,"addressed":false,"structured":false,"dest_mmsi":0,"app_id":0,"data":"116:","radio":0}
+{"class":"AIS","device":"stdin","type":27,"repeat":1,"mmsi":236091959,"scaled":false,"status":3,"accuracy":false,"lon":-92521,"lat":52239,"speed":0,"course":0,"raim":false,"gnss":false}
{"class":"AIS","device":"stdin","type":24,"repeat":0,"mmsi":271040660,"scaled":false,"shipname":"GOZDEM-1","shiptype":37,"vendorid":"1C00045","callsign":"YM5504","to_bow":0,"to_stern":24,"to_port":0,"to_starboard":6}
{"class":"AIS","device":"stdin","type":5,"repeat":0,"mmsi":271010059,"scaled":false,"imo":0,"ais_version":0,"callsign":"TCA2350","shipname":"HEALTH CONTROL 13","shiptype":55,"to_bow":6,"to_stern":10,"to_port":2,"to_starboard":2,"epfd":1,"eta":"00-00T24:60Z","draught":20,"destination":"","dte":0}
{"class":"AIS","device":"stdin","type":5,"repeat":0,"mmsi":271010059,"scaled":false,"imo":0,"ais_version":0,"callsign":"TCA2350","shipname":"HEALTH CONTROL 13","shiptype":55,"to_bow":6,"to_stern":10,"to_port":2,"to_starboard":2,"epfd":1,"eta":"00-00T24:60Z","draught":20,"destination":"","dte":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":276747000,"scaled":false,"seqno":0,"dest_mmsi":2766160,"retransmit":false,"dac":1,"fid":40,"data":"16:\t8"}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":2470052,"scaled":false,"timestamp":"0-00-00T24:60:60Z","accuracy":false,"lon":108600000,"lat":54600000,"epfd":0,"raim":false,"radio":180352}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":2242115,"scaled":false,"timestamp":"2012-06-01T24:60:60Z","accuracy":true,"lon":-5031130,"lat":26021408,"epfd":7,"raim":false,"radio":133322}
+{"class":"AIS","device":"stdin","type":4,"repeat":0,"mmsi":972158237,"scaled":false,"timestamp":"10196-00-24T09:57:37Z","accuracy":true,"lon":123070132,"lat":65599231,"epfd":14,"raim":true,"radio":2248}
+{"class":"AIS","device":"stdin","type":22,"repeat":2,"mmsi":875794037,"scaled":false,"channel_a":3396,"channel_b":373,"txrx":1,"power":false,"dest1":837968222,"dest2":254804543,"addressed":true,"band_a":false,"band_b":true,"zonesize":7}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":2444012,"scaled":false,"seqno":0,"dest_mmsi":255803540,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0,"lat":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442102,"scaled":false,"seqno":0,"dest_mmsi":244100055,"retransmit":true,"dac":1,"fid":30,"linkage":1,"text":"("}
+{"class":"AIS","device":"stdin","type":6,"repeat":0,"mmsi":2444012,"scaled":false,"seqno":0,"dest_mmsi":255803540,"retransmit":true,"dac":1,"fid":18,"linkage":1,"arrival":"00-00T00:00Z","portname":"","destination":"","lon":0,"lat":0}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442101,"scaled":false,"seqno":2,"dest_mmsi":246351000,"retransmit":false,"dac":1,"fid":32,"month":0,"day":0,"tidals":[{"lon":-16777216,"lat":0,"from_hour":0,"from_min":0,"to_hour":0,"to_min":0,"cdir":0,"cspeed":0}]}
+{"class":"AIS","device":"stdin","type":6,"repeat":3,"mmsi":2442104,"scaled":false,"seqno":0,"dest_mmsi":245679000,"retransmit":false,"dac":1,"fid":32,"month":0,"day":0,"tidals":[{"lon":-6291456,"lat":0,"from_hour":0,"from_min":0,"to_hour":0,"to_min":0,"cdir":0,"cspeed":0}]}
diff --git a/www/AIVDM.txt b/www/AIVDM.txt index 9bf9b54b..3909dd6e 100644 --- a/www/AIVDM.txt +++ b/www/AIVDM.txt @@ -4079,7 +4079,7 @@ increased propagation delays associated with long-range detection |95-95 | 1 |Spare | |x|Not used |============================================================================== -Note: Type 27 has not been observed in the wild as of April 2011. +Note: Type 27 is quite rare, and when seen it is rarely 96-bits long. == Local extensions == |