summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2010-01-30 14:19:42 +0000
committerEric S. Raymond <esr@thyrsus.com>2010-01-30 14:19:42 +0000
commitadafdce89464672900c1fcafa45019b73adb715c (patch)
treebcfba55d1488e605f770819bcce291df369b5025
parentcc8599a7f1c56985fc106085e81e6de7a3dbe2ed (diff)
downloadgpsd-adafdce89464672900c1fcafa45019b73adb715c.tar.gz
Improved handling and documentation of AIS type 22 messages.
-rwxr-xr-xdevtools/ais.py23
-rw-r--r--driver_aivdm.c13
-rw-r--r--gps.h18
-rw-r--r--gpsd_json.c64
-rw-r--r--gpsdecode.c42
-rw-r--r--test/sample.aivdm23
-rw-r--r--test/sample.aivdm.chk1
-rw-r--r--www/AIVDM.txt55
8 files changed, 154 insertions, 85 deletions
diff --git a/devtools/ais.py b/devtools/ais.py
index b11d944c..44a8f44e 100755
--- a/devtools/ais.py
+++ b/devtools/ais.py
@@ -15,6 +15,8 @@
# never affect variable-length messages in which the last field type
# is 'string' or 'raw').
# * Does not join the type 21 name extension field to the name fields.
+# * Only handles the broadcast case of type 22. The problem is that the
+# addressed field is located *after* the variant parts. Grrrr...
# * Message type 26 is presently unsupported. It hasn't been observed
# in the wild yet as of Jan 2010; not a lot of point in trying util
# we have test data.
@@ -570,13 +572,13 @@ type22 = (
bitfield("channel_b", 12, 'unsigned', 0, "Channel B"),
bitfield("txrx", 4, 'unsigned', 0, "Tx/Rx mode"),
bitfield("power", 1, 'unsigned', 0, "Power"),
- bitfield("ne_lon", 18, 'unsigned', 0x1a838, "NE Longitude",
+ bitfield("ne_lon", 18, 'signed', 0x1a838, "NE Longitude",
formatter=short_latlon_format),
- bitfield("ne_lat", 17, 'unsigned', 0xd548, "NE Latitude",
+ bitfield("ne_lat", 17, 'signed', 0xd548, "NE Latitude",
formatter=short_latlon_format),
- bitfield("sw_lon", 18, 'unsigned', 0x1a838, "SW Longitude",
+ bitfield("sw_lon", 18, 'signed', 0x1a838, "SW Longitude",
formatter=short_latlon_format),
- bitfield("sw_lat", 17, 'unsigned', 0xd548, "SW Latitude",
+ bitfield("sw_lat", 17, 'signed', 0xd548, "SW Latitude",
formatter=short_latlon_format),
bitfield("addressed", 1, 'unsigned', 0, "Addressed"),
bitfield("band_a", 1, 'unsigned', 0, "Channel A Band"),
@@ -606,13 +608,13 @@ station_type_legends = (
type23 = (
spare(2),
- bitfield("ne_lon", 18, 'unsigned', 0x1a838, "NE Longitude",
+ bitfield("ne_lon", 18, 'signed', 0x1a838, "NE Longitude",
formatter=short_latlon_format),
- bitfield("ne_lat", 17, 'unsigned', 0xd548, "NE Latitude",
+ bitfield("ne_lat", 17, 'signed', 0xd548, "NE Latitude",
formatter=short_latlon_format),
- bitfield("sw_lon", 18, 'unsigned', 0x1a838, "SW Longitude",
+ bitfield("sw_lon", 18, 'signed', 0x1a838, "SW Longitude",
formatter=short_latlon_format),
- bitfield("sw_lat", 17, 'unsigned', 0xd548, "SW Latitude",
+ bitfield("sw_lat", 17, 'signed', 0xd548, "SW Latitude",
formatter=short_latlon_format),
bitfield("stationtype",4, 'unsigned', 0, "Station Type",
validator=lambda n: n >= 0 and n <= 31,
@@ -884,7 +886,7 @@ if __name__ == "__main__":
import sys, getopt
try:
- (options, arguments) = getopt.getopt(sys.argv[1:], "cjst:vx")
+ (options, arguments) = getopt.getopt(sys.argv[1:], "cjst:v")
except getopt.GetoptError, msg:
print "ais.py: " + str(msg)
raise SystemExit, 1
@@ -892,7 +894,6 @@ if __name__ == "__main__":
scaled = False
json = False
csv = False
- skiperr = False
verbose = 0
types = []
for (switch, val) in options:
@@ -910,7 +911,7 @@ if __name__ == "__main__":
verbose += 1
try:
- for (raw, parsed) in parse_ais_messages(sys.stdin, scaled, skiperr, verbose):
+ for (raw, parsed) in parse_ais_messages(sys.stdin, scaled, True, verbose):
if types and parsed[0][1] not in types:
continue
if verbose >= 1:
diff --git a/driver_aivdm.c b/driver_aivdm.c
index 4d0f2383..2265c597 100644
--- a/driver_aivdm.c
+++ b/driver_aivdm.c
@@ -493,11 +493,16 @@ bool aivdm_decode(const char *buf, size_t buflen,
ais->type22.channel_b = UBITS(52, 12);
ais->type22.txrx = UBITS(64, 4);
ais->type22.power = UBITS(68, 1);
- ais->type22.ne_lon = SBITS(69, 18);
- ais->type22.ne_lat = SBITS(87, 17);
- ais->type22.sw_lon = SBITS(104, 18);
- ais->type22.sw_lat = SBITS(122, 17);
ais->type22.addressed = UBITS(139, 1);
+ if (!ais->type22.addressed) {
+ ais->type22.area.ne_lon = SBITS(69, 18);
+ ais->type22.area.ne_lat = SBITS(87, 17);
+ 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.band_a = UBITS(140, 1);
ais->type22.band_b = UBITS(141, 1);
ais->type22.zonesize = UBITS(142, 3);
diff --git a/gps.h b/gps.h
index 50a9966d..7a06c15b 100644
--- a/gps.h
+++ b/gps.h
@@ -747,10 +747,18 @@ struct ais_t
unsigned int txrx; /* transmit/receive mode */
bool power; /* high-power flag */
#define AIS_CHANNEL_LATLON_SCALE 600.0
- int ne_lon; /* NE corner longitude */
- int ne_lat; /* NE corner latitude */
- int sw_lon; /* SW corner longitude */
- int sw_lat; /* SW corner latitude */
+ union {
+ struct {
+ int ne_lon; /* NE corner longitude */
+ int ne_lat; /* NE corner latitude */
+ int sw_lon; /* SW corner longitude */
+ int sw_lat; /* SW corner latitude */
+ } area;
+ struct {
+ unsigned int dest1; /* addressed station MMSI 1 */
+ unsigned int dest2; /* addressed station MMSI 2 */
+ } mmsi;
+ };
bool addressed; /* addressed vs. broadast flag */
bool band_a; /* fix 1.5kHz band for channel A */
bool band_b; /* fix 1.5kHz band for channel B */
@@ -758,11 +766,11 @@ struct ais_t
} type22;
/* Type 23 - Group Assignment Command */
struct {
- //unsigned int 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 */
+ //unsigned int spare; spare bit(s) */
unsigned int stationtype; /* station type code */
unsigned int shiptype; /* ship type code */
//unsigned int spare2; spare bit(s) */
diff --git a/gpsd_json.c b/gpsd_json.c
index a1141d90..becfe3e2 100644
--- a/gpsd_json.c
+++ b/gpsd_json.c
@@ -1183,47 +1183,41 @@ void aivdm_json_dump(const struct ais_t *ais, bool scaled, /*@out@*/char *buf, s
}
break;
case 22: /* Channel Management */
- if (scaled) {
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "\"channel_a\":%u,\"channel_b\":%u,"
+ "\"txrx\":%u,\"power\":%s,",
+ ais->type22.channel_a,
+ ais->type22.channel_b,
+ ais->type22.txrx,
+ JSON_BOOL(ais->type22.power));
+ if (ais->type22.addressed) {
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "\"dest1\":%u,\"dest2\":%u",
+ ais->type22.mmsi.dest1, ais->type22.mmsi.dest2);
+ } else if (scaled) {
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
- "\"channel_a\":%u,\"channel_b\":%u,"
- "\"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.txrx,
- JSON_BOOL(ais->type22.power),
- ais->type22.ne_lon / AIS_CHANNEL_LATLON_SCALE,
- ais->type22.ne_lat / AIS_CHANNEL_LATLON_SCALE,
- ais->type22.sw_lon / AIS_CHANNEL_LATLON_SCALE,
- ais->type22.sw_lat / AIS_CHANNEL_LATLON_SCALE,
- JSON_BOOL(ais->type22.addressed),
- JSON_BOOL(ais->type22.band_a),
- JSON_BOOL(ais->type22.band_b),
- ais->type22.zonesize);
+ "\"sw_lon\":\"%f\",\"sw_lat\":\"%f\",",
+ ais->type22.area.ne_lon / AIS_CHANNEL_LATLON_SCALE,
+ ais->type22.area.ne_lat / AIS_CHANNEL_LATLON_SCALE,
+ ais->type22.area.sw_lon / AIS_CHANNEL_LATLON_SCALE,
+ ais->type22.area.sw_lat / AIS_CHANNEL_LATLON_SCALE);
} else {
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
- "\"channel_a\":%u,\"channel_b\":%u,"
- "\"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.txrx,
- JSON_BOOL(ais->type22.power),
- ais->type22.ne_lon,
- ais->type22.ne_lat,
- ais->type22.sw_lon,
- ais->type22.sw_lat,
- JSON_BOOL(ais->type22.addressed),
- JSON_BOOL(ais->type22.band_a),
- JSON_BOOL(ais->type22.band_b),
- ais->type22.zonesize);
+ "\"sw_lon\":%d,\"sw_lat\":%d,",
+ ais->type22.area.ne_lon,
+ ais->type22.area.ne_lat,
+ ais->type22.area.sw_lon,
+ ais->type22.area.sw_lat);
}
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "\"addressed\":%s,\"band_a\":%s,"
+ "\"band_b\":%s,\"zonesize\":%u}\r\n",
+ JSON_BOOL(ais->type22.addressed),
+ JSON_BOOL(ais->type22.band_a),
+ JSON_BOOL(ais->type22.band_b),
+ ais->type22.zonesize);
break;
case 23: /* Group Assignment Command */
if (scaled) {
diff --git a/gpsdecode.c b/gpsdecode.c
index d566f5a8..37cf6db2 100644
--- a/gpsdecode.c
+++ b/gpsdecode.c
@@ -275,20 +275,34 @@ static void aivdm_csv_dump(struct ais_t *ais, char *buf, size_t buflen)
(uint)ais->type21.virtual_aid);
break;
case 22: /* Channel Management */
- (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
- "%u,%u,%u,%u,%d,%d,%d,%d,%u,%u,%u,%u",
- ais->type22.channel_a,
- ais->type22.channel_b,
- ais->type22.txrx,
- (uint)ais->type22.power,
- ais->type22.ne_lon,
- ais->type22.ne_lat,
- ais->type22.sw_lon,
- ais->type22.sw_lat,
- (uint)ais->type22.addressed,
- (uint)ais->type22.band_a,
- (uint)ais->type22.band_b,
- ais->type22.zonesize);
+ if (!ais->type22.addressed)
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "%u,%u,%u,%u,%d,%d,%d,%d,%u,%u,%u,%u",
+ ais->type22.channel_a,
+ ais->type22.channel_b,
+ ais->type22.txrx,
+ (uint)ais->type22.power,
+ ais->type22.area.ne_lon,
+ ais->type22.area.ne_lat,
+ ais->type22.area.sw_lon,
+ ais->type22.area.sw_lat,
+ (uint)ais->type22.addressed,
+ (uint)ais->type22.band_a,
+ (uint)ais->type22.band_b,
+ ais->type22.zonesize);
+ else
+ (void)snprintf(buf+strlen(buf), buflen-strlen(buf),
+ "%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
+ ais->type22.channel_a,
+ ais->type22.channel_b,
+ ais->type22.txrx,
+ (uint)ais->type22.power,
+ ais->type22.mmsi.dest1,
+ ais->type22.mmsi.dest2,
+ (uint)ais->type22.addressed,
+ (uint)ais->type22.band_a,
+ (uint)ais->type22.band_b,
+ ais->type22.zonesize);
break;
case 23: /* Group Management Command*/
(void)snprintf(buf+strlen(buf), buflen-strlen(buf),
diff --git a/test/sample.aivdm b/test/sample.aivdm
index ac8d56ff..0c3c42df 100644
--- a/test/sample.aivdm
+++ b/test/sample.aivdm
@@ -498,6 +498,29 @@
# Virtual-Aid Flag: 0
# Assigned-Mode Flag: 0
#
+# Type 22:
+# From AISHub. Broadcast case: we won't count this as a full test by
+# itself because the bit layout of the addressed case is different.
+# Verified only by the fact that the Python and C decoders get consistent
+# results, we haven't actually seen an independent dump of these fields.
+# (The noaadata 0.42 dumper for this type doesn't seem to work.)
+!AIVDM,1,1,,A,F030ot22N2P6aoQbhe4736L20000,0*1
+# Message Type : 22
+# Repeat Indicator : 0
+# MMSI : 3160048
+# Channel A : 2087
+# Channel B : 2088
+# Tx/Rx mode : 0
+# Power : 0
+# NE Longitude : -44100
+# NE Latitude : 27330
+# SW Longitude : -48100
+# SW Latitude : 25400
+# Addressed : 0
+# Channel A Band : 0
+# Channel A Band : 0
+# Zone size : 4
+#
# Type 24:
# One pair of type A and Type B messages. Note, these have Coast Guard
# extended fields after them.
diff --git a/test/sample.aivdm.chk b/test/sample.aivdm.chk
index 1f846af9..81ad1ee9 100644
--- a/test/sample.aivdm.chk
+++ b/test/sample.aivdm.chk
@@ -32,4 +32,5 @@
20,3,003669705,2182,5,7,225,0,0,0,0,0,0,0,0,0,0,0,0
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
24,2,338085242,CAPTAIN`S PARADISE,54,ACR1234,WDD7883,8,3,2,1
diff --git a/www/AIVDM.txt b/www/AIVDM.txt
index 1b628fee..cc1fde61 100644
--- a/www/AIVDM.txt
+++ b/www/AIVDM.txt
@@ -1077,8 +1077,8 @@ depending on payload size.
|6-7 | 2 |Repeat Indicator |repeat |As in Common Navigation Block
|8-37 | 30 |Source MMSI |mmsi |Unsigned integer: 9 digits
|38-39 | 2 |Spare | |Not used
-|40-57 | 18 |Longitude |lon |Minutes/10
-|58-74 | 17 |Latitude |lat |Minutes/10
+|40-57 | 18 |Longitude |lon |Signed: minutes/10
+|58-74 | 17 |Latitude |lat |Signed: minutes/10
|75-79 | 5 |Spare | |Not used - reserved
|80-815 |736 |Payload |data |DGNSS correction data
|==============================================================================
@@ -1340,10 +1340,12 @@ network.
|52-63 |12 |Channel B |channel_b |Channel number
|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
-|104-121 |18 |SW Longitude |sw_lon |SW corner longitude to 0.1 minutes
-|122-138 |17 |SW Latitude |sw_lat |SW corner latitude to 0.1 minutes
+|69-86 |18 |NE Longitude |ne_lon |Signed: NE longitude to 0.1 minutes
+|87-103 |17 |NE Latitude |ne_lat |Signed: NE latitude to 0.1 minutes
+|104-121 |18 |SW Longitude |sw_lon |Signed: SW longitude to 0.1 minutes
+|122-138 |17 |SW Latitude |sw_lat |Signed: SW latitude to 0.1 minutes
+|69-98 |30 |MMSI1 |dest1_mmsi|Unsigned: MMSI of destination 1
+|104-133 |30 |MMSI2 |dest1_mmsi|Unsigned: MMSI of destination 2
|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 B Band |band_b |0=Default, 1=12.5kHz
@@ -1351,14 +1353,34 @@ network.
|145-167 |23 |Spare | |Reserved for future use
|==============================================================================
-See <<IALA>> for details on the meaning of these fields. txrx encodes
-the same information as the 2-bit field txrx field in message type 23;
-only the two low bits are used.
+The values of the channel_a and channel_b fields are ITU frequency
+designators for channelas A and B. Normally these will be 2087 and
+2088, the AIS 1 and AIS 2 frequencies of 87B (161.975 MHz) and 88B
+(162.025 MHz) respectively. Regional authorities may set different
+frequencies.
-Note that the 'not available' values for longitudesc and latitudes
+The txrx field encodes the same information as the 2-bit field txrx
+field in message type 23; only the two low bits are used.
+
+The power bit instructs designated recxeivers which power level to use.
+
+If the message is broadcast (addressed field is 0), the ne_lon,
+ne_lat, sw_lon, and sw_lat fields are the corners of a rectangular
+jurisdiction area over which control parameters are to be set. If it
+is addressed (addressed field is 1), the same span of data is
+interpreted as two 30-bit MMSIs beginning at at bit offsets 69 and 104
+respectively.
+
+Note that the 'not available' values for longitude and latitudes
match the short ones used in message 17, not the long ones used in the
common navigation block and elsewhere.
+The band fields control channel bandwidth for channels A and B, and
+the zonesize field describes the size of the transition zone around the
+control juisdiction. The semantics of these fields is complicated,
+controlling transponder behavior as it moves between jurisdictions;
+see <<IALA>> for full details.
+
=== Type 23: Group Assignment Command ===
This message is intended to be broadcast by a competent authority (an
@@ -1376,10 +1398,10 @@ station network.
|6-7 | 2 |Repeat Indicator |repeat |As in Common Navigation Block
|8-37 |30 |MMSI |mmsi |Unsigned Integer: 9 digits
|38-39 | 2 |Spare | |Not used
-|40-57 |18 |NE Longitude |ne_lon |Same as type 22
-|58-74 |17 |NE Latitude |ne_lat |Same as type 22
-|75-92 |18 |SW Longitude |sw_lon |Same as type 22
-|93-109 |17 |SW Latitude |sw_lat |Same as type 22
+|40-57 |18 |NE Longitude |ne_lon |Same as broadcast type 22
+|58-74 |17 |NE Latitude |ne_lat |Same as broadcast type 22
+|75-92 |18 |SW Longitude |sw_lon |Same as broadcast type 22
+|93-109 |17 |SW Latitude |sw_lat |Same as broadcast type 22
|110-113 | 4 |Station Type |station_type |Unsigned Integer, see below
|114-121 | 8 |Ship Type |ship_type |As for Message type 5
|122-143 |22 |Spare | |Not used
@@ -1393,7 +1415,7 @@ 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
+Note that the 'not available' values for longitude and latitudes
match the short ones used in messages 17 and 22, not the long ones
used in the common navigation block and elsewhere.
@@ -1826,4 +1848,5 @@ for AIS messages type 25 and 26, not yet observed in the wild.
Version 1.22 describes the problem with message length checks.
Notes on EPFD value 15 and shiptype values > 99 are added. Added
-another AIS feed.
+another AIS feed. Corrections and more details on message 22.
+