summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2009-09-22 23:10:08 +0000
committerEric S. Raymond <esr@thyrsus.com>2009-09-22 23:10:08 +0000
commita450f4ae94b6c266f8062c2a9544479aecda1ac3 (patch)
treed80bdc1460c4f6cbce84d65bce991fbdc40f91f6
parent0b87f38c11d786a3ebeddeac536ea3e62ad31998 (diff)
downloadgpsd-a450f4ae94b6c266f8062c2a9544479aecda1ac3.tar.gz
Fully support AIS type 22 and 23 messages. All regression tests pass.
Codebase splints clean.
-rw-r--r--TODO2
-rw-r--r--ais_json.c2
-rw-r--r--driver_aivdm.c27
-rw-r--r--gps.h17
-rw-r--r--gpsd_json.c62
-rw-r--r--gpsdecode.c15
-rwxr-xr-xjsongen.py20
-rw-r--r--www/AIVDM.txt11
8 files changed, 139 insertions, 17 deletions
diff --git a/TODO b/TODO
index b58dcbc8..31ce271d 100644
--- a/TODO
+++ b/TODO
@@ -42,8 +42,6 @@ of mode switch we should be uttering.
*** Cleanup after the big protocol rewrite
-**** The Python client library presently speaks only old protocol.
-
**** cgps.c still relies on old protocol (that's Jeff Francis's problem).
**** Profiling features (Z and $) need to be ported from the old protocol.
diff --git a/ais_json.c b/ais_json.c
index d2f9c552..2f43444a 100644
--- a/ais_json.c
+++ b/ais_json.c
@@ -128,6 +128,8 @@ int json_ais_read(const char *buf,
} else if (strstr(buf, "\"type\":21,") != NULL) {
status = json_read_object(buf, json_ais21, endptr);
} else if (strstr(buf, "\"type\":22,") != NULL) {
+ status = json_read_object(buf, json_ais23, endptr);
+ } else if (strstr(buf, "\"type\":23,") != NULL) {
status = json_read_object(buf, json_ais22, endptr);
} else if (strstr(buf, "\"type\":24,") != NULL) {
status = json_read_object(buf, json_ais24, endptr);
diff --git a/driver_aivdm.c b/driver_aivdm.c
index d45d3f08..59359bc0 100644
--- a/driver_aivdm.c
+++ b/driver_aivdm.c
@@ -472,7 +472,32 @@ bool aivdm_decode(const char *buf, size_t buflen,
ais->type19.lat,
ais->type19.second);
break;
- case 24: /* Type 24 - Class B CS Static Data Report */
+ case 22: /* Channel Management */
+ ais->type22.channel_a = UBITS(40, 12);
+ ais->type22.channel_b = UBITS(52, 12);
+ ais->type22.txrx = UBITS(64, 4);
+ ais->type22.power = UBITS(68, 1);
+ ais->type22.ne_lon = UBITS(69, 18);
+ ais->type22.ne_lat = UBITS(87, 17);
+ ais->type22.sw_lon = UBITS(104, 18);
+ ais->type22.sw_lat = UBITS(122, 17);
+ ais->type22.addressed = UBITS(139, 1);
+ ais->type22.band_a = UBITS(140, 1);
+ ais->type22.band_b = UBITS(141, 1);
+ ais->type22.zonesize = UBITS(142, 3);
+ break;
+ case 23: /* Group Assignment Command */
+ ais->type23.ne_lon = UBITS(40, 18);
+ ais->type23.ne_lat = UBITS(58, 17);
+ ais->type23.sw_lon = UBITS(75, 18);
+ ais->type23.sw_lat = UBITS(93, 17);
+ ais->type23.stationtype = UBITS(110, 4);
+ ais->type23.shiptype = UBITS(114, 8);
+ ais->type23.txrx = UBITS(144, 4);
+ ais->type23.interval = UBITS(146, 4);
+ ais->type23.quiet = UBITS(150, 4);
+ break;
+ case 24: /* Class B CS Static Data Report */
switch (UBITS(38, 2)) {
case 0:
UCHARS(40, ais_context->shipname);
diff --git a/gps.h b/gps.h
index 7c60926b..db4b83dd 100644
--- a/gps.h
+++ b/gps.h
@@ -746,7 +746,7 @@ struct ais_t
//uint spare; spare bit(s) */
uint channel_a; /* Channel A number */
uint channel_b; /* Channel B number */
- uint mode; /* transmit/receive mode */
+ uint txrx; /* transmit/receive mode */
bool power; /* high-power flag */
#define AIS_CHANNEL_LATLON_SCALE 600.0
int ne_lon; /* NE corner longitude */
@@ -758,6 +758,21 @@ struct ais_t
bool band_b; /* fix 1.5kHz band for channel B */
uint zonesize; /* size of transitional zone */
} type22;
+ /* Type 23 - Group Assignment Command */
+ struct {
+ //uint spare; spare bit(s) */
+ int ne_lon; /* NE corner longitude */
+ int ne_lat; /* NE corner latitude */
+ int sw_lon; /* SW corner longitude */
+ int sw_lat; /* SW corner latitude */
+ uint stationtype; /* station type code */
+ uint shiptype; /* ship type code */
+ //uint spare2; spare bit(s) */
+ uint txrx; /* transmit-enable code */
+ uint interval; /* report interval */
+ uint quiet; /* quiet time */
+ //uint spare3; spare bit(s) */
+ } type23;
/* Type 24 - Class B CS Static Data Report */
struct {
char shipname[AIS_SHIPNAME_MAXLEN+1]; /* vessel name */
diff --git a/gpsd_json.c b/gpsd_json.c
index 38c58e63..f4f1ec82 100644
--- a/gpsd_json.c
+++ b/gpsd_json.c
@@ -597,6 +597,27 @@ void aivdm_json_dump(const struct ais_t *ais, bool scaled, /*@out@*/char *buf, s
#define SHIPTYPE_DISPLAY(n) (((n) < (uint)NITEMS(ship_type_legends)) ? ship_type_legends[n] : "INVALID SHIP TYPE")
+ static char *station_type_legends[16] = {
+ "All types of mobiles",
+ "Reserved for future use",
+ "All types of Class B mobile stations",
+ "SAR airborne mobile station",
+ "Aid to Navigation station",
+ "Class B shipborne mobile station",
+ "Regional use and inland waterways",
+ "Regional use and inland waterways",
+ "Regional use and inland waterways",
+ "Regional use and inland waterways",
+ "Reserved for future use",
+ "Reserved for future use",
+ "Reserved for future use",
+ "Reserved for future use",
+ "Reserved for future use",
+ "Reserved for future use",
+ };
+
+#define STATIONTYPE_DISPLAY(n) (((n) < (uint)NITEMS(ship_type_legends)) ? station_type_legends[n] : "INVALID STATION TYPE")
+
static char *navaid_type_legends[] = {
"Unspcified",
"Reference point",
@@ -1124,14 +1145,14 @@ void aivdm_json_dump(const struct ais_t *ais, bool scaled, /*@out@*/char *buf, s
if (scaled) {
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
"\"channel_a\":%u,\"channel_b\":%u,"
- "\"mode\":%u,\"power\":%s,"
+ "\"txrx\":%u,\"power\":%s,"
"\"ne_lon\":\"%f\",\"ne_lat\":\"%f\","
"\"sw_lon\":\"%f\",\"sw_lat\":\"%f\","
"\"addressed\":%s,\"band_a\":%s,"
"\"band_b\":%s,\"zonesize\":\":%u}\r\n",
ais->type22.channel_a,
ais->type22.channel_b,
- ais->type22.mode,
+ ais->type22.txrx,
JSON_BOOL(ais->type22.power),
ais->type22.ne_lon / AIS_CHANNEL_LATLON_SCALE,
ais->type22.ne_lat / AIS_CHANNEL_LATLON_SCALE,
@@ -1144,14 +1165,14 @@ void aivdm_json_dump(const struct ais_t *ais, bool scaled, /*@out@*/char *buf, s
} else {
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
"\"channel_a\":%u,\"channel_b\":%u,"
- "\"mode\":%u,\"power\":%s,"
+ "\"txrx\":%u,\"power\":%s,"
"\"ne_lon\":%d,\"ne_lat\":%d,"
"\"sw_lon\":%d,\"sw_lat\":%d,"
"\"addressed\":%s,\"band_a\":%s,"
"\"band_b\":%s,\"zonesize\":\":%u}\r\n",
ais->type22.channel_a,
ais->type22.channel_b,
- ais->type22.mode,
+ ais->type22.txrx,
JSON_BOOL(ais->type22.power),
ais->type22.ne_lon,
ais->type22.ne_lat,
@@ -1163,7 +1184,38 @@ void aivdm_json_dump(const struct ais_t *ais, bool scaled, /*@out@*/char *buf, s
ais->type22.zonesize);
}
break;
- case 24: /* Class B CS Static Data Report */
+ case 23: /* Group Assignment Command */
+ if (scaled) {
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "\"ne_lon\":\"%f\",\"ne_lat\":\"%f\","
+ "\"sw_lon\":\"%f\",\"sw_lat\":\"%f\","
+ "\"stationtype\":%s,\"shiptype\":%s,"
+ "\"interval\":%u,\"quiet\":%u\r\n",
+ ais->type23.ne_lon / AIS_CHANNEL_LATLON_SCALE,
+ ais->type23.ne_lat / AIS_CHANNEL_LATLON_SCALE,
+ ais->type23.sw_lon / AIS_CHANNEL_LATLON_SCALE,
+ ais->type23.sw_lat / AIS_CHANNEL_LATLON_SCALE,
+ STATIONTYPE_DISPLAY(ais->type23.stationtype),
+ SHIPTYPE_DISPLAY(ais->type23.shiptype),
+ ais->type23.interval,
+ ais->type23.quiet);
+ } else {
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "\"ne_lon\":%d,\"ne_lat\":%d,"
+ "\"sw_lon\":%d,\"sw_lat\":%d,"
+ "\"stationtype\":%u,\"shiptype\":%u,"
+ "\"interval\":%u,\"quiet\":%u\r\n",
+ ais->type23.ne_lon,
+ ais->type23.ne_lat,
+ ais->type23.sw_lon,
+ ais->type23.sw_lat,
+ ais->type23.stationtype,
+ ais->type23.shiptype,
+ ais->type23.interval,
+ ais->type23.quiet);
+ }
+ break;
+ case 24: /* Class B CS Static Data Report */
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
"\"shipname\":\"%s\",",
json_stringify(buf1,sizeof(buf1), ais->type24.shipname));
diff --git a/gpsdecode.c b/gpsdecode.c
index eccfb045..406f651c 100644
--- a/gpsdecode.c
+++ b/gpsdecode.c
@@ -280,7 +280,7 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen)
"%u,%u,%u,%u,%d,%d,%d,%d,%u,%u,%u,%u",
ais->type22.channel_a,
ais->type22.channel_b,
- ais->type22.mode,
+ ais->type22.txrx,
(uint)ais->type22.power,
ais->type22.ne_lon,
ais->type22.ne_lat,
@@ -291,6 +291,19 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen)
(uint)ais->type22.band_b,
ais->type22.zonesize);
break;
+ case 23: /* Group Management Command*/
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "%d,%d,%d,%d,%u,%u,%u,%u,%u",
+ ais->type23.ne_lon,
+ ais->type23.ne_lat,
+ ais->type23.sw_lon,
+ ais->type23.sw_lat,
+ ais->type23.stationtype,
+ ais->type23.shiptype,
+ ais->type23.txrx,
+ ais->type23.interval,
+ ais->type23.quiet);
+ break;
case 24: /* Class B CS Static Data Report */
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
"%s,",
diff --git a/jsongen.py b/jsongen.py
index f1a06652..be1dafd9 100755
--- a/jsongen.py
+++ b/jsongen.py
@@ -312,7 +312,7 @@ ais_specs = (
# fieldname type default
('channel_a', 'uinteger', '0'),
('channel_b', 'uinteger', '0'),
- ('mode', 'uinteger', '0'),
+ ('txrx', 'uinteger', '0'),
('power', 'boolean', 'false'),
('ne_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
('ne_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
@@ -324,7 +324,23 @@ ais_specs = (
('zonesize', 'uinteger', '0'),
),
},
- # Structure of mesage 23 is a mystery
+ {
+ "initname" : "json_ais23",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type23",
+ "fieldmap":(
+ # fieldname type default
+ ('ne_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('ne_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('sw_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('sw_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('stationtype', 'uinteger', '0'),
+ ('shiptype', 'uinteger', '0'),
+ ('txrx', 'uinteger', '0'),
+ ('interval', 'uinteger', '0'),
+ ('quiet', 'uinteger', '0'),
+ ),
+ },
{
"initname" : "json_ais24",
"header": "\tAIS_HEADER,",
diff --git a/www/AIVDM.txt b/www/AIVDM.txt
index 6c0d1391..92ada1f7 100644
--- a/www/AIVDM.txt
+++ b/www/AIVDM.txt
@@ -1222,7 +1222,7 @@ Field Len Description Member Units
38-39 2 Spare Not used
40-51 12 Channel A channel_a Channel number
52-63 12 Channel B channel_b Channel number
-64-67 4 Tx/Rx mode mode Transmit/receive mode
+64-67 4 Tx/Rx mode txrx Transmit/receive mode
68-68 1 Power power Low=0, high=1
69-86 18 NE Longitude ne_lon NE corner longitude to 0.1 minutes
87-103 17 NE Latitude ne_lat NE corner latitude to 0.1 minutes
@@ -1230,7 +1230,7 @@ Field Len Description Member Units
122-138 17 SW Latitude sw_lat SW corner latitude to 0.1 minutes
139-139 1 Addressed addressed 0=Broadcast, 1=Addressed
140-140 1 Channel A Band band_a 0=Default, 1=12.5kHz
-141-141 1 Channel A Band band_a 0=Default, 1=12.5kHz
+141-141 1 Channel B Band band_b 0=Default, 1=12.5kHz
142-144 3 Zone size zonesize Size of transitional zone
145-167 23 Spare Reserved for future use
--------------------------------------------------------------------------------
@@ -1245,7 +1245,7 @@ common navigation block and elsewhere.
This message is intended to be broadcast by a competent authority (an
AIS network-control base station) to set to set operational parameters
-for all mobile stations in an AIS coverage region. Length is 160 bits.
+for all mobile stations in an AIS coverage region. Length is 160 bits.
This message contains no navigational information, and is unlikely to
be of interest unless you are implementing or studying an AIS base
@@ -1271,8 +1271,9 @@ Field Len Description Member Units
154-159 6 Spare
--------------------------------------------------------------------------------
-The target set of mobikle stations is specified by the station-type
-and ship-type fields.
+The target set of mobile stations is specified by the station-type and
+ship-type fields. An addressed (non-broadcast) message 22 overrides a
+message 23, but a message 23 ovewrrides a broadcast message 22.
Note that the 'not available' values for longitudesc and latitudes
match the short ones used in messages 17 and 22, not the long ones