diff options
-rw-r--r-- | ais_json.c | 28 | ||||
-rw-r--r-- | driver_ais.c | 24 | ||||
-rw-r--r-- | gps.h | 27 | ||||
-rw-r--r-- | gpsd_json.c | 71 | ||||
-rw-r--r-- | jsongen.py.in | 40 | ||||
-rw-r--r-- | www/AIVDM.txt | 63 |
6 files changed, 217 insertions, 36 deletions
@@ -330,6 +330,34 @@ int json_ais_read(const char *buf, status = json_read_object(buf, json_ais8_fid19, endptr); imo = true; } + else if (strstr(buf, "\"fid\":23,") != NULL) { + status = json_read_object(buf, json_ais8_fid23, endptr); + ais->type8.dac200fid23.start_year = AIS_YEAR_NOT_AVAILABLE; + ais->type8.dac200fid23.start_month = AIS_MONTH_NOT_AVAILABLE; + ais->type8.dac200fid23.start_day = AIS_DAY_NOT_AVAILABLE; + ais->type8.dac200fid23.start_hour = AIS_HOUR_NOT_AVAILABLE; + ais->type8.dac200fid23.start_minute = AIS_MINUTE_NOT_AVAILABLE; + ais->type8.dac200fid23.end_year = AIS_YEAR_NOT_AVAILABLE; + ais->type8.dac200fid23.end_month = AIS_MONTH_NOT_AVAILABLE; + ais->type8.dac200fid23.end_day = AIS_DAY_NOT_AVAILABLE; + ais->type8.dac200fid23.end_hour = AIS_HOUR_NOT_AVAILABLE; + ais->type8.dac200fid23.end_minute = AIS_MINUTE_NOT_AVAILABLE; + // cppcheck-suppress uninitvar + (void)sscanf(start, "%09u-%02u-%02uT%02u:%02u", + &ais->type8.dac200fid23.start_year, + &ais->type8.dac200fid23.start_month, + &ais->type8.dac200fid23.start_day, + &ais->type8.dac200fid23.start_hour, + &ais->type8.dac200fid23.start_minute); + // cppcheck-suppress uninitvar + (void)sscanf(end, "%09u-%02u-%02uT%02u:%02u", + &ais->type8.dac200fid23.end_year, + &ais->type8.dac200fid23.end_month, + &ais->type8.dac200fid23.end_day, + &ais->type8.dac200fid23.end_hour, + &ais->type8.dac200fid23.end_minute); + imo = true; + } else if (strstr(buf, "\"fid\":27,") != NULL) { status = json_read_object(buf, json_ais8_fid27, endptr); if (status == 0) { diff --git a/driver_ais.c b/driver_ais.c index 4e566749..d3452255 100644 --- a/driver_ais.c +++ b/driver_ais.c @@ -653,6 +653,30 @@ bool ais_binary_decode(const int debug, ais->type8.dac200fid10.course_q = (bool)UBITS(158, 1); ais->type8.dac200fid10.heading_q = (bool)UBITS(159, 1); /* skip 8 bits */ + imo = true; + break; + case 23: + ais->type8.dac200fid23.start_year = UBITS(56, 8); + ais->type8.dac200fid23.start_month = UBITS(64, 4); + ais->type8.dac200fid23.start_day = UBITS(68, 5); + ais->type8.dac200fid23.end_year = UBITS(73, 8); + ais->type8.dac200fid23.end_month = UBITS(81, 4); + ais->type8.dac200fid23.end_day = UBITS(85, 5); + ais->type8.dac200fid23.start_hour = UBITS(90, 5); + ais->type8.dac200fid23.start_minute = UBITS(95, 6); + ais->type8.dac200fid23.end_hour = UBITS(101, 5); + ais->type8.dac200fid23.end_minute = UBITS(106, 6); + ais->type8.dac200fid23.start_lon = SBITS(112, 28); + ais->type8.dac200fid23.start_lat = SBITS(140, 27); + ais->type8.dac200fid23.end_lon = SBITS(167, 28); + ais->type8.dac200fid23.end_lat = SBITS(195, 27); + ais->type8.dac200fid23.type = UBITS(222, 4); + ais->type8.dac200fid23.min = SBITS(226, 9); + ais->type8.dac200fid23.max = SBITS(235, 9); + ais->type8.dac200fid23.class = UBITS(244, 2); + ais->type8.dac200fid23.wind = UBITS(246, 4); + /* skip 6 bits */ + imo = true; break; } } @@ -1261,6 +1261,33 @@ struct ais_t bool course_q; /* Course inf. quality */ bool heading_q; /* Heading inf. quality */ } dac200fid10; + /* Inland AIS EMMA Warning */ + struct { + unsigned int start_year; /* Start Year */ + unsigned int start_month; /* Start Month */ + unsigned int start_day; /* Start Day */ + unsigned int end_year; /* End Year */ + unsigned int end_month; /* End Month */ + unsigned int end_day; /* End Day */ + unsigned int start_hour; /* Start Hour */ + unsigned int start_minute; /* Start Minute */ + unsigned int end_hour; /* End Hour */ + unsigned int end_minute; /* End Minute */ + signed int start_lon; /* Start Longitude */ + signed int start_lat; /* Start Latitude */ + signed int end_lon; /* End Longitude */ + signed int end_lat; /* End Latitude */ + unsigned int type; /* Type */ +#define DAC200FID23_TYPE_UNKNOWN 0 + signed int min; /* Min value */ +#define DAC200FID23_MIN_UNKNOWN 255 + signed int max; /* Max value */ +#define DAC200FID23_MAX_UNKNOWN 255 + unsigned int class; /* Classification */ +#define DAC200FID23_CLASS_UNKNOWN 0 + unsigned int wind; /* Wind Direction */ +#define DAC200FID23_WIND_UNKNOWN 0 + } dac200fid23; /* IMO236 - Meteorological-Hydrological data * Trial message, not to be used after January 2013 * Replaced by IMO289 (DAC 1, FID 31) diff --git a/gpsd_json.c b/gpsd_json.c index 7866f87a..722b8e09 100644 --- a/gpsd_json.c +++ b/gpsd_json.c @@ -2735,6 +2735,37 @@ void json_aivdm_dump(const struct ais_t *ais, "Loaded", }; #define LSTATUS_DISPLAY(n) (((n) < (unsigned int)NITEMS(lstatus_types)) ? lstatus_types[n] : "INVALID LOAD STATUS") + const char *emma_types[] = { + "Not Available", + "Wind", + "Rain", + "Snow and ice", + "Thunderstorm", + "Fog", + "Low temperature", + "High temperature", + "Flood", + "Forest Fire", + }; +#define EMMA_TYPE_DISPLAY(n) (((n) < (unsigned int)NITEMS(emma_types)) ? emma_types[n] : "INVALID EMMA TYPE") + const char *emma_classes[] = { + "Slight", + "Medium", + "Strong", + }; +#define EMMA_CLASS_DISPLAY(n) (((n) < (unsigned int)NITEMS(emma_classes)) ? emma_classes[n] : "INVALID EMMA TYPE") + const char *emma_winds[] = { + "N/A", + "North", + "North East", + "East", + "South East", + "South", + "South West", + "West", + "North West", + }; +#define EMMA_WIND_DISPLAY(n) (((n) < (unsigned int)NITEMS(emma_winds)) ? emma_winds[n] : "INVALID EMMA WIND DIRECTION") switch (ais->type8.fid) { case 10: /* Inland ship static and voyage-related data */ for (cp = shiptypes; cp < shiptypes + NITEMS(shiptypes); cp++) @@ -2765,6 +2796,46 @@ void json_aivdm_dump(const struct ais_t *ais, JSON_BOOL(ais->type8.dac200fid10.heading_q)); structured = true; break; + case 23: /* EMMA warning */ + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"start\":\"%4u-%02u-%02uT%02u:%02u\"," + "\"end\":\"%4u-%02u-%02uT%02u:%02u\",", + ais->type8.dac200fid23.start_year + 2000, + ais->type8.dac200fid23.start_month, + ais->type8.dac200fid23.start_hour, + ais->type8.dac200fid23.start_minute, + ais->type8.dac200fid23.start_day, + ais->type8.dac200fid23.end_year + 2000, + ais->type8.dac200fid23.end_month, + ais->type8.dac200fid23.end_day, + ais->type8.dac200fid23.end_hour, + ais->type8.dac200fid23.end_minute); + if (scaled) + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"start_lon\":%.4f,\"start_lat\":%.4f,\"end_lon\":%.4f,\"end_lat\":%.4f,", + ais->type8.dac200fid23.start_lon / AIS_LATLON_DIV, + ais->type8.dac200fid23.start_lat / AIS_LATLON_DIV, + ais->type8.dac200fid23.end_lon / AIS_LATLON_DIV, + ais->type8.dac200fid23.end_lat / AIS_LATLON_DIV); + else + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"start_lon\":%d,\"start_lat\":%d,\"end_lon\":%d,\"end_lat\":%d,", + ais->type8.dac200fid23.start_lon, + ais->type8.dac200fid23.start_lat, + ais->type8.dac200fid23.end_lon, + ais->type8.dac200fid23.end_lat); + (void)snprintf(buf + strlen(buf), buflen - strlen(buf), + "\"type\":%u,\"type_text\":\"%s\",\"min\":%d,\"max\":%d,\"class\":%u,\"class_text\":\"%s\",\"wind\":%u,\"wind_text\":\"%s\"}", + + ais->type8.dac200fid23.type, + EMMA_TYPE_DISPLAY(ais->type8.dac200fid23.type), + ais->type8.dac200fid23.min, + ais->type8.dac200fid23.max, + ais->type8.dac200fid23.class, + EMMA_CLASS_DISPLAY(ais->type8.dac200fid23.class), + ais->type8.dac200fid23.wind, + EMMA_WIND_DISPLAY(ais->type8.dac200fid23.wind)); + break; } } if (!structured) diff --git a/jsongen.py.in b/jsongen.py.in index 27492af7..8a64c6dc 100644 --- a/jsongen.py.in +++ b/jsongen.py.in @@ -430,7 +430,7 @@ ais_specs = ( ('thour', 'uinteger', 'AIS_HOUR_NOT_AVAILABLE'), ('tminute', 'uinteger', 'AIS_MINUTE_NOT_AVAILABLE'), ), - 'stringbuffered' : ('from', 'to'), + 'stringbuffered' : ('closefrom', 'closeto'), }, { "initname" : "json_ais8_fid15", @@ -491,6 +491,29 @@ ais_specs = ( ), }, { + "initname" : "json_ais8_fid23", + "headers": ("AIS_HEADER","AIS_TYPE8"), + "structname": "ais->type8.dac200fid23", + "fieldmap":( + # fieldname type default + ('start', 'string', None), + ('end', 'string', None), + ('start_lon', 'integer', 'AIS_LON4_NOT_AVAILABLE'), + ('start_lat', 'integer', 'AIS_LAT4_NOT_AVAILABLE'), + ('end_lon', 'integer', 'AIS_LON4_NOT_AVAILABLE'), + ('end_lat', 'integer', 'AIS_LAT4_NOT_AVAILABLE'), + ('type', 'uinteger', 'DAC200FID23_TYPE_UNKNOWN'), + ('type_text', 'ignore', None), + ('min', 'integer', 'DAC200FID23_MIN_UNKNOWN'), + ('max', 'integer', 'DAC200FID23_MAX_UNKNOWN'), + ('class', 'uinteger', 'DAC200FID23_CLASS_UNKNOWN'), + ('class_text', 'ignore', None), + ('wind', 'uinteger', 'DAC200FID23_WIND_UNKNOWN'), + ('wind_text', 'ignore', None), + ), + 'stringbuffered' : ('start', 'end'), + }, + { "initname" : "json_ais8_fid27", "headers": ("AIS_HEADER","AIS_TYPE8",), "structname": "ais->type8.dac1fid27", @@ -879,11 +902,16 @@ def generate(spec): initname = spec["initname"] # Utter storage declarations for any fields that are declared to be # stringbuffered. These will need to be postprocessed in json_ais_read(). - for (attr, itype, default) in spec["fieldmap"]: - if attr in spec.get("stringbuffered", []): - if attr not in outboard: - report += " char %s[JSON_VAL_MAX+1];\n" % attr - outboard.append(attr) + attributes = [t[0] for t in spec["fieldmap"]] + for attr in spec.get("stringbuffered", []): + if attr not in attributes: + print >>sys.stderr, "buffered %s is not in base attributes of %s"\ + % (attr, initname) + raise SystemExit, 1 + elif attr not in outboard: + report += " char %s[JSON_VAL_MAX+1];\n" % attr + outboard.append(attr) + structname = spec["structname"] # If there are structarrays describing array subobjects, we need # to make a separate parse control initializer for each one. The diff --git a/www/AIVDM.txt b/www/AIVDM.txt index 7d333ca4..8bd26eed 100644 --- a/www/AIVDM.txt +++ b/www/AIVDM.txt @@ -3605,37 +3605,37 @@ replace the Notices to Skippers warnings. [frame="topbot",options="header"] |=============================================================================== -|Field |Len |Description |Member |T|Units -|0-5 | 6 |Message Type |type |u|Constant: 8 -|6-7 | 2 |Repeat Indicator |repeat |u|As in Common Navigation Block -|8-37 | 30 |Source MMSI |mmsi |u|9 decimal digits -|38-39 | 2 |Spare | |x|Not used -|40-49 | 10 |Designated Area Code|dac |u|Constant: 200 -|50-55 | 6 |Functional ID |fid |u|Constant: 23 -|56-63 | 8 |Start Year |start_year |u|1-55, year since 2000 - 0 = N/A (default) -|64-67 | 4 |Start Month |start_month|u|1-12; 0 = N/A (default) -|68-72 | 5 |Start Day |start_day |u|1-31; 0 = N/A (default) -|73-80 | 8 |End Year |end_year |u|1-55, year since 2000 +|Field |Len |Description |Member |T|Units +|0-5 | 6 |Message Type |type |u|Constant: 8 +|6-7 | 2 |Repeat Indicator |repeat |u|As in Common Navigation Block +|8-37 | 30 |Source MMSI |mmsi |u|9 decimal digits +|38-39 | 2 |Spare | |x|Not used +|40-49 | 10 |Designated Area Code|dac |u|Constant: 200 +|50-55 | 6 |Functional ID |fid |u|Constant: 23 +|56-63 | 8 |Start Year |start_year |u|1-55, year since 2000 + 0 = N/A (default) +|64-67 | 4 |Start Month |start_month |u|1-12; 0 = N/A (default) +|68-72 | 5 |Start Day |start_day |u|1-31; 0 = N/A (default) +|73-80 | 8 |End Year |end_year |u|1-55, year since 2000 0 = N/A (default) -|81-84 | 4 |End Month |end_month |u|1-12; 0 = N/A (default) -|85-89 | 5 |End Day |end_day |u|1-31; 0 = N/A (default) -|90-94 | 5 |Start Hour |start_hour |u|0-23; 24 = N/A (default) -|95-100 | 6 |Start Minute |start_min |u|0-59; 60 = N/A (default) -|101-105 | 5 |End Hour |end_hour |u|0-23; 24 = N/A (default) -|106-111 | 6 |End Minute |end_min |u|0-59; 60 = N/A (default) -|112-139 | 28 |Start Longitude |start_lon |I4|Minutes/10000 (as in CNB) -|140-166 | 27 |Start Latitude |start_lat |I4|Minutes/10000 (as in CNB) -|167-194 | 28 |End Longitude |end_lon |I4|Minutes/10000 (as in CNB) -|195-221 | 27 |End Latitude |end_lat |I4|Minutes/10000 (as in CNB) -|222-225 | 4 |Type |type |e|See "EMMA Type Codes" below -|226-234 | 9 |Min value |min |i|Signed Integer, see below -|235-243 | 9 |Max value |max |i|Signed Integer, see below -|244-245 | 2 |Classification |class |e|1 = Slight, - 2 = Medium, - 3 = Strong -|246-249 | 4 |Wind Direction |wind |e|See "EMMA Winds" below -|250-255 | 6 |Spare | |x|Not used +|81-84 | 4 |End Month |end_month |u|1-12; 0 = N/A (default) +|85-89 | 5 |End Day |end_day |u|1-31; 0 = N/A (default) +|90-94 | 5 |Start Hour |start_hour |u|0-23; 24 = N/A (default) +|95-100 | 6 |Start Minute |start_minute|u|0-59; 60 = N/A (default) +|101-105 | 5 |End Hour |end_hour |u|0-23; 24 = N/A (default) +|106-111 | 6 |End Minute |end_minute |u|0-59; 60 = N/A (default) +|112-139 | 28 |Start Longitude |start_lon |I4|Minutes/10000 (as in CNB) +|140-166 | 27 |Start Latitude |start_lat |I4|Minutes/10000 (as in CNB) +|167-194 | 28 |End Longitude |end_lon |I4|Minutes/10000 (as in CNB) +|195-221 | 27 |End Latitude |end_lat |I4|Minutes/10000 (as in CNB) +|222-225 | 4 |Type |type |e|See "EMMA Type Codes" below +|226-234 | 9 |Min value |min |i|Signed Integer, see below +|235-243 | 9 |Max value |max |i|Signed Integer, see below +|244-245 | 2 |Classification |class |e|1 = Slight, + 2 = Medium, + 3 = Strong +|246-249 | 4 |Wind Direction |wind |e|See "EMMA Winds" below +|250-255 | 6 |Spare | |x|Not used |=============================================================================== OPEN-QUESTION: <<INLAND>> is not explicit about the interpretation of @@ -3657,6 +3657,7 @@ are UTC or local. .EMMA Type Codes [frame="topbot",options="header"] |============================== +| 0 | NA | Not Available | 1 | WI | Wind | 2 | RA | Rain | 3 | SN | Snow and ice @@ -4812,6 +4813,8 @@ ISO8601 format. |8(1/13)| tmonth,tday,thour,tminute | to | %02u-%02uT%02u:%02uZ |8(1/22)| month,day,hour,minute | timestamp | %02u-%02uT%02u:%02uZ |8(1/27)| month,day,hour,minute | start | %02u-%02uT%02u:%02uZ +|8(200/23)| year,month,day,hour,minute | start |%4u-%02u-%02uT%02u:%02u +|8(200/23)| year,month,day,hour,minute | end |%4u-%02u-%02uT%02u:%02u |=========================================================================== 4. There are two variants of the encoding, one scaled and one |