/* * Driver for AIS/AIVDM messages. * * See the file AIVDM.txt on the GPSD website for documentation and references. * * Code for message types 1-15, 18-21, and 24 has been tested against * live data with known-good decodings. Code for message types 16-17, * 22-23, and 25-26 has not. * * This file is Copyright (c) 2010 by the GPSD project * BSD terms apply: see the file COPYING in the distribution root for details. */ #include #include #include #include "gpsd.h" #include "bits.h" /** * Parse the data from the device */ static void from_sixbit(char *bitvec, uint start, int count, char *to) { /*@ +type @*/ #ifdef S_SPLINT_S /* the real string causes a splint internal error */ const char sixchr[] = "abcd"; #else const char sixchr[64] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^- !\"#$%&'()*+,-./0123456789:;<=>?"; #endif /* S_SPLINT_S */ int i; char newchar; /* six-bit to ASCII */ for (i = 0; i < count - 1; i++) { newchar = sixchr[ubits(bitvec, start + 6 * i, 6U)]; if (newchar == '@') break; else to[i] = newchar; } to[i] = '\0'; /* trim spaces on right end */ for (i = count - 2; i >= 0; i--) if (to[i] == ' ' || to[i] == '@') to[i] = '\0'; else break; /*@ -type @*/ } /*@ +charint -fixedformalarray -usedef -branchstate @*/ bool aivdm_decode(const char *buf, size_t buflen, struct aivdm_context_t ais_contexts[AIVDM_CHANNELS], struct ais_t *ais) { #ifdef __UNUSED_DEBUG__ char *sixbits[64] = { "000000", "000001", "000010", "000011", "000100", "000101", "000110", "000111", "001000", "001001", "001010", "001011", "001100", "001101", "001110", "001111", "010000", "010001", "010010", "010011", "010100", "010101", "010110", "010111", "011000", "011001", "011010", "011011", "011100", "011101", "011110", "011111", "100000", "100001", "100010", "100011", "100100", "100101", "100110", "100111", "101000", "101001", "101010", "101011", "101100", "101101", "101110", "101111", "110000", "110001", "110010", "110011", "110100", "110101", "110110", "110111", "111000", "111001", "111010", "111011", "111100", "111101", "111110", "111111", }; #endif /* __UNUSED_DEBUG__ */ int nfrags, ifrag, nfields = 0; unsigned char *field[NMEA_MAX*2]; unsigned char fieldcopy[NMEA_MAX*2+1]; unsigned char *data, *cp = fieldcopy; unsigned char ch, pad; struct aivdm_context_t *ais_context; int i; if (buflen == 0) return false; /* we may need to dump the raw packet */ gpsd_report(LOG_PROG, "AIVDM packet length %zd: %s\n", buflen, buf); /* first clear the result, making sure we don't return garbage */ memset(ais, 0, sizeof(*ais)); /* discard overlong sentences */ if (strlen(buf) > sizeof(fieldcopy)-1) { gpsd_report(LOG_ERROR, "overlong AIVDM packet.\n"); return false; } /* extract packet fields */ (void)strlcpy((char *)fieldcopy, buf, sizeof(fieldcopy)); field[nfields++] = (unsigned char *)buf; for (cp = fieldcopy; cp < fieldcopy + buflen; cp++) if (*cp == ',') { *cp = '\0'; field[nfields++] = cp + 1; } /* discard sentences with exiguous commas; catches run-ons */ if (nfields < 7) { gpsd_report(LOG_ERROR, "malformed AIVDM packet.\n"); return false; } switch (field[4][0]) { /* FIXME: if fields[4] == "12", it doesn't detect the error */ case '\0': /* * Apparently an empty channel is normal for AIVDO sentences, * which makes sense as they don't come in over radio. This * is going to break if there's ever an AIVDO type 24, though. */ if (strncmp((const char *)field[0], "!AIVDO", 6) != 0) gpsd_report(LOG_ERROR, "invalid empty AIS channel. Assuming 'A'\n"); ais_context = &ais_contexts[0]; break; case '1': gpsd_report(LOG_ERROR, "invalid AIS channel 0x%0x '%c'. Assuming 'A'\n", field[4][0], (field[4][0] != '\0' ? field[4][0]:' ')); /*@fallthrough@*/ case 'A': ais_context = &ais_contexts[0]; break; case '2': gpsd_report(LOG_ERROR, "invalid AIS channel '2'. Assuming 'B'.\n"); /*@fallthrough@*/ case 'B': ais_context = &ais_contexts[1]; break; default: gpsd_report(LOG_ERROR, "invalid AIS channel 0x%0X .\n", field[4][0]); return false; } nfrags = atoi((char *)field[1]); /* number of fragments to expect */ ifrag = atoi((char *)field[2]); /* fragment id */ data = field[5]; pad = field[6][0]; /* number of padding bits */ gpsd_report(LOG_PROG, "nfrags=%d, ifrag=%d, decoded_frags=%d, data=%s\n", nfrags, ifrag, ais_context->decoded_frags, data); /* assemble the binary data */ /* check fragment ordering */ if (ifrag != ais_context->decoded_frags + 1) { gpsd_report(LOG_ERROR, "invalid fragment #%d received, expected #%d.\n", ifrag, ais_context->decoded_frags + 1); if (ifrag != 1) return false; /* else, ifrag==1: Just discard all that was previously decoded and * simply handle that packet */ ais_context->decoded_frags = 0; } if (ifrag == 1) { (void)memset(ais_context->bits, '\0', sizeof(ais_context->bits)); ais_context->bitlen = 0; } /* wacky 6-bit encoding, shades of FIELDATA */ /*@ +charint @*/ for (cp = data; cp < data + strlen((char *)data); cp++) { ch = *cp; ch -= 48; if (ch >= 40) ch -= 8; #ifdef __UNUSED_DEBUG__ gpsd_report(LOG_RAW, "%c: %s\n", *cp, sixbits[ch]); #endif /* __UNUSED_DEBUG__ */ /*@ -shiftnegative @*/ for (i = 5; i >= 0; i--) { if ((ch >> i) & 0x01) { ais_context->bits[ais_context->bitlen / 8] |= (1 << (7 - ais_context->bitlen % 8)); } ais_context->bitlen++; } /*@ +shiftnegative @*/ } if (isdigit(pad)) ais_context->bitlen -= (pad - '0'); /* ASCII assumption */ /*@ -charint @*/ /* time to pass buffered-up data to where it's actually processed? */ if (ifrag == nfrags) { size_t clen = (ais_context->bitlen + 7) / 8; gpsd_report(LOG_INF, "AIVDM payload is %zd bits, %zd chars: %s\n", ais_context->bitlen, clen, gpsd_hexdump_wrapper(ais_context->bits, clen, LOG_INF)); /* clear waiting fragments count */ ais_context->decoded_frags = 0; #define BITS_PER_BYTE 8 #define UBITS(s, l) ubits((char *)ais_context->bits, s, l) #define SBITS(s, l) sbits((char *)ais_context->bits, s, l) #define UCHARS(s, to) from_sixbit((char *)ais_context->bits, s, sizeof(to), to) ais->type = UBITS(0, 6); ais->repeat = UBITS(6, 2); ais->mmsi = UBITS(8, 30); gpsd_report(LOG_INF, "AIVDM message type %d, MMSI %09d:\n", ais->type, ais->mmsi); /* * Something about the shape of this switch statement confuses * GNU indent so badly that there is no point in trying to be * finer-grained than leaving it all alone. */ /* *INDENT-OFF* */ switch (ais->type) { case 1: /* Position Report */ case 2: case 3: if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type %d size not 168 bits (%zd).\n", ais->type, ais_context->bitlen); return false; } ais->type1.status = UBITS(38, 4); ais->type1.turn = SBITS(42, 8); ais->type1.speed = UBITS(50, 10); ais->type1.accuracy = (bool)UBITS(60, 1); ais->type1.lon = SBITS(61, 28); ais->type1.lat = SBITS(89, 27); ais->type1.course = UBITS(116, 12); ais->type1.heading = UBITS(128, 9); ais->type1.second = UBITS(137, 6); ais->type1.maneuver = UBITS(143, 2); //ais->type1.spare = UBITS(145, 3); ais->type1.raim = UBITS(148, 1)!=0; ais->type1.radio = UBITS(149, 20); gpsd_report(LOG_INF, "Nav=%d TURN=%d SPEED=%d Q=%d Lon=%d Lat=%d COURSE=%d TH=%d Sec=%d\n", ais->type1.status, ais->type1.turn, ais->type1.speed, (uint)ais->type1.accuracy, ais->type1.lon, ais->type1.lat, ais->type1.course, ais->type1.heading, ais->type1.second); break; case 4: /* Base Station Report */ case 11: /* UTC/Date Response */ if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type %d size not 168 bits (%zd).\n", ais->type, ais_context->bitlen); return false; } ais->type4.year = UBITS(38, 14); ais->type4.month = UBITS(52, 4); ais->type4.day = UBITS(56, 5); ais->type4.hour = UBITS(61, 5); ais->type4.minute = UBITS(66, 6); ais->type4.second = UBITS(72, 6); ais->type4.accuracy = UBITS(78, 1)!=0; ais->type4.lon = SBITS(79, 28); ais->type4.lat = SBITS(107, 27); ais->type4.epfd = UBITS(134, 4); //ais->type4.spare = UBITS(138, 10); ais->type4.raim = UBITS(148, 1)!=0; ais->type4.radio = UBITS(149, 19); gpsd_report(LOG_INF, "Date: %4d:%02d:%02dT%02d:%02d:%02d Q=%d Lat=%d Lon=%d epfd=%d\n", ais->type4.year, ais->type4.month, ais->type4.day, ais->type4.hour, ais->type4.minute, ais->type4.second, (uint)ais->type4.accuracy, ais->type4.lat, ais->type4.lon, ais->type4.epfd); break; case 5: /* Ship static and voyage related data */ if (ais_context->bitlen != 424) { gpsd_report(LOG_WARN, "AIVDM message type 5 size not 424 bits (%zd).\n", ais_context->bitlen); return false; } ais->type5.ais_version = UBITS(38, 2); ais->type5.imo = UBITS(40, 30); UCHARS(70, ais->type5.callsign); UCHARS(112, ais->type5.shipname); ais->type5.shiptype = UBITS(232, 8); ais->type5.to_bow = UBITS(240, 9); ais->type5.to_stern = UBITS(249, 9); ais->type5.to_port = UBITS(258, 6); ais->type5.to_starboard = UBITS(264, 6); ais->type5.epfd = UBITS(270, 4); ais->type5.month = UBITS(274, 4); ais->type5.day = UBITS(278, 5); ais->type5.hour = UBITS(283, 5); ais->type5.minute = UBITS(288, 6); ais->type5.draught = UBITS(294, 8); UCHARS(302, ais->type5.destination); ais->type5.dte = UBITS(422, 1); //ais->type5.spare = UBITS(423, 1); gpsd_report(LOG_INF, "AIS=%d callsign=%s, name=%s destination=%s\n", ais->type5.ais_version, ais->type5.callsign, ais->type5.shipname, ais->type5.destination); break; case 6: /* Addressed Binary Message */ if (ais_context->bitlen < 88 || ais_context->bitlen > 1008) { gpsd_report(LOG_WARN, "AIVDM message type 6 size is out of range (%zd).\n", ais_context->bitlen); return false; } ais->type6.seqno = UBITS(38, 2); ais->type6.dest_mmsi = UBITS(40, 30); ais->type6.retransmit = (bool)UBITS(70, 1); //ais->type6.spare = UBITS(71, 1); ais->type6.dac = UBITS(72, 10); ais->type6.fid = UBITS(82, 6); ais->type6.bitcount = ais_context->bitlen - 88; (void)memcpy(ais->type6.bitdata, (char *)ais_context->bits + (88 / BITS_PER_BYTE), (ais->type6.bitcount + 7) / 8); gpsd_report(LOG_INF, "seqno=%d, dest=%u, dac=%u, fid=%u, cnt=%zd\n", ais->type6.seqno, ais->type6.dest_mmsi, ais->type6.dac, ais->type6.fid, ais->type6.bitcount); break; case 7: /* Binary acknowledge */ case 13: /* Safety Related Acknowledge */ { unsigned int u, mmsi[4]; if (ais_context->bitlen < 72 || ais_context->bitlen > 168) { gpsd_report(LOG_WARN, "AIVDM message type %d size is out of range (%zd).\n", ais->type, ais_context->bitlen); return false; } for (u = 0; u < sizeof(mmsi)/sizeof(mmsi[0]); u++) if (ais_context->bitlen > 40 + 32*u) mmsi[u] = UBITS(40 + 32*u, 30); else mmsi[u] = 0; /*@ -usedef @*/ ais->type7.mmsi1 = mmsi[0]; ais->type7.mmsi2 = mmsi[1]; ais->type7.mmsi3 = mmsi[2]; ais->type7.mmsi4 = mmsi[3]; /*@ +usedef @*/ gpsd_report(LOG_INF, "\n"); break; } case 8: /* Binary Broadcast Message */ if (ais_context->bitlen < 56 || ais_context->bitlen > 1008) { gpsd_report(LOG_WARN, "AIVDM message type 8 size is out of range (%zd).\n", ais_context->bitlen); return false; } //ais->type8.spare = UBITS(38, 2); ais->type8.dac = UBITS(40, 10); ais->type8.fid = UBITS(40, 6); ais->type8.bitcount = ais_context->bitlen - 56; #ifdef __UNUSED__ if (ais->type8.dac == 1) /* * The strange order in which these FIDs occur is * direct from IMO circular 289. */ switch (ais->type8.fid) { case 31: /* Meteorological-Hydrological data */ ais->type8.dac1fid31.lon = SBITS(56, 25); ais->type8.dac1fid31.lat = SBITS(81, 24); ais->type8.dac1fid31.accuracy = (bool)UBITS(105, 1); ais->type8.dac1fid31.day = UBITS(106, 5); ais->type8.dac1fid31.hour = UBITS(111, 5); ais->type8.dac1fid31.minute = UBITS(116, 6); ais->type8.dac1fid31.wspeed = UBITS(122, 7); ais->type8.dac1fid31.wgust = UBITS(129, 7); ais->type8.dac1fid31.wdir = UBITS(129, 7); ais->type8.dac1fid31.wgustdir = UBITS(136, 7); ais->type8.dac1fid31.temperature = SBITS(143, 11); ais->type8.dac1fid31.humidity = UBITS(154, 7); ais->type8.dac1fid31.dewpoint = SBITS(154, 10); ais->type8.dac1fid31.pressure = UBITS(164, 9); ais->type8.dac1fid31.pressuretend = UBITS(173, 3); ais->type8.dac1fid31.visgreater = (bool)UBITS(174, 1); ais->type8.dac1fid31.visibility = UBITS(175, 8); ais->type8.dac1fid31.waterlevel = UBITS(183, 12); goto nocopy; case 25: /* Dangerous Cargo Indication */ goto nocopy; case 32: /* Tidal Window */ goto nocopy; case 24: /* Extended ship static & voyage-related data */ goto nocopy; case 16: /* Number of persons on board */ goto nocopy; case 17: /* VTS-generated/synthetic targets */ goto nocopy; case 18: /* Clearance time to enter port */ goto nocopy; case 19: /* Marine Traffic Signal */ goto nocopy; case 20: /* Berthing Data */ goto nocopy; case 21: /* Weather observation report from ship */ goto nocopy; case 22: /* Area notice - broadcast */ goto nocopy; case 23: /* Area notice - addressed */ goto nocopy; case 27: /* Route information - broadcast */ goto nocopy; case 29: /* Text Description - broadcast */ goto nocopy; case 30: /* Text Description - addressed */ goto nocopy; } /* land here if we failed to match a known DAC/FID */ #endif /* __UNUSED_ */ (void)memcpy(ais->type8.bitdata, (char *)ais_context->bits + (56 / BITS_PER_BYTE), (ais->type8.bitcount + 7) / 8); #ifdef __UNUSED__ nocopy: #endif /* __UNUSED_ */ gpsd_report(LOG_INF, "dac=%u, fid=%u, cnt=%zd\n", ais->type8.dac, ais->type8.fid, ais->type8.bitcount); break; case 9: /* Standard SAR Aircraft Position Report */ if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type 9 size not 168 bits (%zd).\n", ais_context->bitlen); return false; } ais->type9.alt = UBITS(38, 12); ais->type9.speed = UBITS(50, 10); ais->type9.accuracy = (bool)UBITS(60, 1); ais->type9.lon = SBITS(61, 28); ais->type9.lat = SBITS(89, 27); ais->type9.course = UBITS(116, 12); ais->type9.second = UBITS(128, 6); ais->type9.regional = UBITS(134, 8); ais->type9.dte = UBITS(142, 1); //ais->type9.spare = UBITS(143, 3); ais->type9.assigned = UBITS(146, 1)!=0; ais->type9.raim = UBITS(147, 1)!=0; ais->type9.radio = UBITS(148, 19); gpsd_report(LOG_INF, "Alt=%d SPEED=%d Q=%d Lon=%d Lat=%d COURSE=%d Sec=%d\n", ais->type9.alt, ais->type9.speed, (uint)ais->type9.accuracy, ais->type9.lon, ais->type9.lat, ais->type9.course, ais->type9.second); break; case 10: /* UTC/Date inquiry */ if (ais_context->bitlen != 72) { gpsd_report(LOG_WARN, "AIVDM message type 10 size not 72 bits (%zd).\n", ais_context->bitlen); return false; } //ais->type10.spare = UBITS(38, 2); ais->type10.dest_mmsi = UBITS(40, 30); //ais->type10.spare2 = UBITS(70, 2); gpsd_report(LOG_INF, "dest=%u\n", ais->type10.dest_mmsi); break; case 12: /* Safety Related Message */ if (ais_context->bitlen < 72 || ais_context->bitlen > 1008) { gpsd_report(LOG_WARN, "AIVDM message type 12 size is out of range (%zd).\n", ais_context->bitlen); return false; } ais->type12.seqno = UBITS(38, 2); ais->type12.dest_mmsi = UBITS(40, 30); ais->type12.retransmit = (bool)UBITS(70, 1); //ais->type12.spare = UBITS(71, 1); from_sixbit((char *)ais_context->bits, 72, ais_context->bitlen-72, ais->type12.text); gpsd_report(LOG_INF, "seqno=%d, dest=%u\n", ais->type12.seqno, ais->type12.dest_mmsi); break; case 14: /* Safety Related Broadcast Message */ if (ais_context->bitlen < 40 || ais_context->bitlen > 1008) { gpsd_report(LOG_WARN, "AIVDM message type 14 size is out of range (%zd).\n", ais_context->bitlen); return false; } //ais->type14.spare = UBITS(38, 2); from_sixbit((char *)ais_context->bits, 40, ais_context->bitlen-40, ais->type14.text); gpsd_report(LOG_INF, "\n"); break; case 15: /* Interrogation */ if (ais_context->bitlen < 88 || ais_context->bitlen > 168) { gpsd_report(LOG_WARN, "AIVDM message type 15 size is out of range (%zd).\n", ais_context->bitlen); return false; } (void)memset(&ais->type15, '\0', sizeof(ais->type15)); //ais->type14.spare = UBITS(38, 2); ais->type15.mmsi1 = UBITS(40, 30); ais->type15.type1_1 = UBITS(70, 6); ais->type15.type1_1 = UBITS(70, 6); ais->type15.offset1_1 = UBITS(76, 12); //ais->type14.spare2 = UBITS(88, 2); if (ais_context->bitlen > 90) { ais->type15.type1_2 = UBITS(90, 6); ais->type15.offset1_2 = UBITS(96, 12); //ais->type14.spare3 = UBITS(108, 2); if (ais_context->bitlen > 110) { ais->type15.mmsi2 = UBITS(110, 30); ais->type15.type2_1 = UBITS(140, 6); ais->type15.offset2_1 = UBITS(146, 12); //ais->type14.spare4 = UBITS(158, 2); } } gpsd_report(LOG_INF, "\n"); break; case 16: /* Assigned Mode Command */ if (ais_context->bitlen != 96 && ais_context->bitlen != 144) { gpsd_report(LOG_WARN, "AIVDM message type 16 size is out of range (%zd).\n", ais_context->bitlen); return false; } ais->type16.mmsi1 = UBITS(40, 30); ais->type16.offset1 = UBITS(70, 12); ais->type16.increment1 = UBITS(82, 10); if (ais_context->bitlen < 144) ais->type16.mmsi2=ais->type16.offset2=ais->type16.increment2 = 0; else { ais->type16.mmsi2 = UBITS(92, 30); ais->type16.offset2 = UBITS(122, 12); ais->type16.increment2 = UBITS(134, 10); } gpsd_report(LOG_INF, "\n"); break; case 17: /* GNSS Broadcast Binary Message */ if (ais_context->bitlen < 80 || ais_context->bitlen > 816) { gpsd_report(LOG_WARN, "AIVDM message type 17 size is out of range (%zd).\n", ais_context->bitlen); return false; } //ais->type17.spare = UBITS(38, 2); ais->type17.lon = UBITS(40, 18); ais->type17.lat = UBITS(58, 17); //ais->type17.spare = UBITS(75, 4); ais->type17.bitcount = ais_context->bitlen - 80; (void)memcpy(ais->type17.bitdata, (char *)ais_context->bits + (80 / BITS_PER_BYTE), (ais->type17.bitcount + 7) / 8); gpsd_report(LOG_INF, "\n"); break; case 18: /* Standard Class B CS Position Report */ if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type 18 size not 168 bits (%zd).\n", ais_context->bitlen); return false; } ais->type18.reserved = UBITS(38, 8); ais->type18.speed = UBITS(46, 10); ais->type18.accuracy = UBITS(56, 1)!=0; ais->type18.lon = SBITS(57, 28); ais->type18.lat = SBITS(85, 27); ais->type18.course = UBITS(112, 12); ais->type18.heading = UBITS(124, 9); ais->type18.second = UBITS(133, 6); ais->type18.regional = UBITS(139, 2); ais->type18.cs = UBITS(141, 1)!=0; ais->type18.display = UBITS(142, 1)!=0; ais->type18.dsc = UBITS(143, 1)!=0; ais->type18.band = UBITS(144, 1)!=0; ais->type18.msg22 = UBITS(145, 1)!=0; ais->type18.assigned = UBITS(146, 1)!=0; ais->type18.raim = UBITS(147, 1)!=0; ais->type18.radio = UBITS(148, 20); gpsd_report(LOG_INF, "reserved=%d speed=%d accuracy=%d lon=%d lat=%d course=%d heading=%d sec=%d\n", ais->type18.reserved, ais->type18.speed, (uint)ais->type18.accuracy, ais->type18.lon, ais->type18.lat, ais->type18.course, ais->type18.heading, ais->type18.second); break; case 19: /* Extended Class B CS Position Report */ if (ais_context->bitlen != 312) { gpsd_report(LOG_WARN, "AIVDM message type 19 size not 312 bits (%zd).\n", ais_context->bitlen); return false; } ais->type19.reserved = UBITS(38, 8); ais->type19.speed = UBITS(46, 10); ais->type19.accuracy = UBITS(56, 1)!=0; ais->type19.lon = SBITS(57, 28); ais->type19.lat = SBITS(85, 27); ais->type19.course = UBITS(112, 12); ais->type19.heading = UBITS(124, 9); ais->type19.second = UBITS(133, 6); ais->type19.regional = UBITS(139, 4); UCHARS(143, ais->type19.shipname); ais->type19.shiptype = UBITS(263, 8); ais->type19.to_bow = UBITS(271, 9); ais->type19.to_stern = UBITS(280, 9); ais->type19.to_port = UBITS(289, 6); ais->type19.to_starboard = UBITS(295, 6); ais->type19.epfd = UBITS(299, 4); ais->type19.raim = UBITS(302, 1)!=0; ais->type19.dte = UBITS(305, 1)!=0; ais->type19.assigned = UBITS(306, 1)!=0; //ais->type19.spare = UBITS(307, 5); gpsd_report(LOG_INF, "reserved=%d speed=%d accuracy=%d lon=%d lat=%d course=%d heading=%d sec=%d name=%s\n", ais->type19.reserved, ais->type19.speed, (uint)ais->type19.accuracy, ais->type19.lon, ais->type19.lat, ais->type19.course, ais->type19.heading, ais->type19.second, ais->type19.shipname); break; case 20: /* Data Link Management Message */ if (ais_context->bitlen < 72 || ais_context->bitlen > 160) { gpsd_report(LOG_WARN, "AIVDM message type 20 size is out of range (%zd).\n", ais_context->bitlen); return false; } //ais->type20.spare = UBITS(38, 2); ais->type20.offset1 = UBITS(40, 12); ais->type20.number1 = UBITS(52, 4); ais->type20.timeout1 = UBITS(56, 3); ais->type20.increment1 = UBITS(59, 11); ais->type20.offset2 = UBITS(70, 12); ais->type20.number2 = UBITS(82, 4); ais->type20.timeout2 = UBITS(86, 3); ais->type20.increment2 = UBITS(89, 11); ais->type20.offset3 = UBITS(100, 12); ais->type20.number3 = UBITS(112, 4); ais->type20.timeout3 = UBITS(116, 3); ais->type20.increment3 = UBITS(119, 11); ais->type20.offset4 = UBITS(130, 12); ais->type20.number4 = UBITS(142, 4); ais->type20.timeout4 = UBITS(146, 3); ais->type20.increment4 = UBITS(149, 11); break; case 21: /* Aid-to-Navigation Report */ if (ais_context->bitlen < 272 || ais_context->bitlen > 360) { gpsd_report(LOG_WARN, "AIVDM message type 21 size is out of range (%zd).\n", ais_context->bitlen); return false; } ais->type21.aid_type = UBITS(38, 5); from_sixbit((char *)ais_context->bits, 43, 21, ais->type21.name); if (strlen(ais->type21.name) == 20 && ais_context->bitlen > 272) from_sixbit((char *)ais_context->bits, 272, (ais_context->bitlen - 272)/6, ais->type21.name+20); ais->type21.accuracy = UBITS(163, 1); ais->type21.lon = SBITS(164, 28); ais->type21.lat = SBITS(192, 27); ais->type21.to_bow = UBITS(219, 9); ais->type21.to_stern = UBITS(228, 9); ais->type21.to_port = UBITS(237, 6); ais->type21.to_starboard = UBITS(243, 6); ais->type21.epfd = UBITS(249, 4); ais->type21.second = UBITS(253, 6); ais->type21.off_position = UBITS(259, 1)!=0; ais->type21.regional = UBITS(260, 8); ais->type21.raim = UBITS(268, 1)!=0; ais->type21.virtual_aid = UBITS(269, 1)!=0; ais->type21.assigned = UBITS(270, 1)!=0; //ais->type21.spare = UBITS(271, 1); gpsd_report(LOG_INF, "name=%s accuracy=%d lon=%d lat=%d sec=%d\n", ais->type21.name, (uint)ais->type19.accuracy, ais->type19.lon, ais->type19.lat, ais->type19.second); break; case 22: /* Channel Management */ if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type 22 size not 168 bits (%zd).\n", ais_context->bitlen); return false; } 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.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); break; case 23: /* Group Assignment Command */ if (ais_context->bitlen != 160) { gpsd_report(LOG_WARN, "AIVDM message type 23 size not 160 bits (%zd).\n", ais_context->bitlen); return false; } ais->type23.ne_lon = SBITS(40, 18); ais->type23.ne_lat = SBITS(58, 17); ais->type23.sw_lon = SBITS(75, 18); ais->type23.sw_lat = SBITS(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: if (ais_context->bitlen != 160) { gpsd_report(LOG_WARN, "AIVDM message type 24A size not 160 bits (%zd).\n", ais_context->bitlen); return false; } if (ais_context->mmsi24) { gpsd_report(LOG_WARN, "AIVDM message type 24 collision on channel %c : Discarding previous sentence 24A from %09u.\n", field[4][0], ais_context->mmsi24); /* no return false */ } ais_context->mmsi24 = ais->mmsi; UCHARS(40, ais_context->shipname24); //ais->type24.a.spare = UBITS(160, 8); gpsd_report(LOG_INF, "subtype=A name=%s\n", ais_context->shipname24); return false; /* data only partially decoded */ case 1: if (ais_context->bitlen != 168) { gpsd_report(LOG_WARN, "AIVDM message type 24B size not 168 bits (%zd).\n", ais_context->bitlen); return false; } if (ais_context->mmsi24 != ais->mmsi) { if (ais_context->mmsi24) gpsd_report(LOG_WARN, "AIVDM message type 24 collision on channel %c: MMSI mismatch: %09u vs %09u.\n", field[4][0], ais_context->mmsi24, ais->mmsi); else gpsd_report(LOG_WARN, "AIVDM message type 24 collision on channel %c: 24B sentence from %09u without 24A.\n", field[4][0], ais->mmsi); return false; } (void)strlcpy(ais->type24.shipname, ais_context->shipname24, sizeof(ais_context->shipname24)); ais->type24.shiptype = UBITS(40, 8); UCHARS(48, ais->type24.vendorid); UCHARS(90, ais->type24.callsign); if (AIS_AUXILIARY_MMSI(ais->mmsi)) { ais->type24.mothership_mmsi = UBITS(132, 30); gpsd_report(LOG_INF, "subtype=B subsubtype=aux " "shiptype=%u vendor=%s callsign=%s " "mothership=%09u\n", ais->type24.shiptype, ais->type24.vendorid, ais->type24.callsign, ais->type24.mothership_mmsi); } else { ais->type24.dim.to_bow = UBITS(132, 9); ais->type24.dim.to_stern = UBITS(141, 9); ais->type24.dim.to_port = UBITS(150, 6); ais->type24.dim.to_starboard = UBITS(156, 6); gpsd_report(LOG_INF, "subtype=B subsubtype=dim " "shiptype=%u vendor=%s callsign=%s " "bow=%u stern=%u port=%u starboard=%u\n", ais->type24.shiptype, ais->type24.vendorid, ais->type24.callsign, ais->type24.dim.to_bow, ais->type24.dim.to_stern, ais->type24.dim.to_port, ais->type24.dim.to_starboard); } //ais->type24.b.spare = UBITS(162, 8); ais_context->mmsi24 = 0; /* reset last know 24A for collision detection */ break; default: gpsd_report(LOG_WARN, "AIVDM message type 24 of subtype unknown.\n"); gpsd_report(LOG_INF, "\n"); return false; } break; case 25: /* Binary Message, Single Slot */ /* this check and the following one reject line noise */ if (ais_context->bitlen < 40 || ais_context->bitlen > 168) { gpsd_report(LOG_WARN, "AIVDM message type 25 size not between 40 to 168 bits (%zd).\n", ais_context->bitlen); return false; } ais->type25.addressed = (bool)UBITS(38, 1); ais->type25.structured = (bool)UBITS(39, 1); if (ais_context->bitlen < (unsigned)(40 + (16*ais->type25.structured) + (30*ais->type25.addressed))) { gpsd_report(LOG_WARN, "AIVDM message type 25 too short for mode.\n"); return false; } if (ais->type25.addressed) ais->type25.dest_mmsi = UBITS(40, 30); if (ais->type25.structured) ais->type25.app_id = UBITS(40+ais->type25.addressed*30,16); /* * Not possible to do this right without machinery we * don't yet have. The problem is that if the addressed * bit is on the bitfield start won't be on a byte * boundary. Thus the formulas below (and in message type 26) * will work perfectly for brodacst messages, but for addressed * messages the retrieved data will be led by thr 30 bits of * the destination MMSI */ ais->type25.bitcount = ais_context->bitlen - 40 - 16*ais->type25.structured; (void)memcpy(ais->type25.bitdata, (char *)ais_context->bits+5 + 2 * ais->type25.structured, (ais->type25.bitcount + 7) / 8); gpsd_report(LOG_INF, "addressed=%d, structured=%d, dest=%u, id=%u, cnt=%zd\n", ais->type25.addressed, ais->type25.structured, ais->type25.dest_mmsi, ais->type25.app_id, ais->type25.bitcount); break; case 26: /* Binary Message, Multiple Slot */ if (ais_context->bitlen < 60 || ais_context->bitlen > 1004) { gpsd_report(LOG_WARN, "AIVDM message type 26 size is out of range (%zd).\n", ais_context->bitlen); return false; } ais->type26.addressed = (bool)UBITS(38, 1); ais->type26.structured = (bool)UBITS(39, 1); if ((signed)ais_context->bitlen < 40 + 16*ais->type26.structured + 30*ais->type26.addressed + 20) { gpsd_report(LOG_WARN, "AIVDM message type 26 too short for mode.\n"); return false; } if (ais->type26.addressed) ais->type26.dest_mmsi = UBITS(40, 30); if (ais->type26.structured) ais->type26.app_id = UBITS(40+ais->type26.addressed*30,16); ais->type26.bitcount = ais_context->bitlen - 60 - 16*ais->type26.structured; (void)memcpy(ais->type26.bitdata, (char *)ais_context->bits+5 + 2 * ais->type26.structured, (ais->type26.bitcount + 7) / 8); gpsd_report(LOG_INF, "addressed=%d, structured=%d, dest=%u, id=%u, cnt=%zd\n", ais->type26.addressed, ais->type26.structured, ais->type26.dest_mmsi, ais->type26.app_id, ais->type26.bitcount); break; default: gpsd_report(LOG_INF, "\n"); gpsd_report(LOG_ERROR, "Unparsed AIVDM message type %d.\n",ais->type); return false; } /* *INDENT-ON* */ #undef UCHARS #undef SBITS #undef UBITS #undef BITS_PER_BYTE /* data is fully decoded */ return true; } /* we're still waiting on another sentence */ ais_context->decoded_frags++; return false; } /*@ -charint +fixedformalarray +usedef +branchstate @*/ /* driver_aivdm.c ends here */