コード例 #1
0
/*@ +charint @*/
gps_mask_t evermore_parse(struct gps_device_t * session, unsigned char *buf,
			  size_t len)
{
    unsigned char buf2[MAX_PACKET_LENGTH], *cp, *tp;
    size_t i, datalen;
    unsigned int type, used, visible, satcnt, j, k;
    double version;
    gps_mask_t mask = 0;

    if (len == 0)
	return 0;

    /* time to unstuff it and discard the header and footer */
    cp = buf + 2;
    tp = buf2;
    if (*cp == 0x10)
	cp++;
    datalen = (size_t) * cp++;

    gpsd_report(LOG_RAW, "raw EverMore packet type 0x%02x, length %zd: %s\n",
		*cp, len, gpsd_hexdump_wrapper(buf, len, LOG_RAW));

    datalen -= 2;

    /*@ -usedef @*/
    for (i = 0; i < (size_t) datalen; i++) {
	*tp = *cp++;
	if (*tp == 0x10)
	    cp++;
	tp++;
    }

    type = (unsigned char)getub(buf2, 1);
    /*@ +usedef @*/

    /*@ -usedef -compdef @*/
    gpsd_report(LOG_RAW, "EverMore packet type 0x%02x, length %zd: %s\n",
		type, datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_RAW));
    /*@ +usedef +compdef @*/

    (void)snprintf(session->gpsdata.tag, sizeof(session->gpsdata.tag),
		   "EID%u", type);

    session->cycle_end_reliable = true;

    switch (type) {
    case 0x02:			/* Navigation Data Output */
	session->context->gps_week = (unsigned short)getleuw(buf2, 2);
	session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
	/*@ ignore @*//*@ splint is confused @ */
	session->newdata.time =
	    gpstime_to_unix(session->context->gps_week,
			    session->context->gps_tow) -
	    session->context->leap_seconds;
	/*@ end @*/
	ecef_to_wgs84fix(&session->newdata, &session->gpsdata.separation,
			 getlesl(buf2, 8) * 1.0, getlesl(buf2, 12) * 1.0,
			 getlesl(buf2, 16) * 1.0, getlesw(buf2, 20) / 10.0,
			 getlesw(buf2, 22) / 10.0, getlesw(buf2, 24) / 10.0);
	used = (unsigned char)getub(buf2, 26) & 0x0f;
	//visible = (getub(buf2, 26) & 0xf0) >> 4;
	version = (uint) getleuw(buf2, 27) / 100.0;
	/* that's all the information in this packet */
	if (used < 3)
	    session->newdata.mode = MODE_NO_FIX;
	else if (used == 3)
	    session->newdata.mode = MODE_2D;
	else {
	    session->newdata.mode = MODE_3D;
	    mask |= ALTITUDE_IS | CLIMB_IS;
	}
	mask |= TIME_IS | LATLON_IS | TRACK_IS | SPEED_IS | MODE_IS;
	if (session->subtype[0] == '\0') {
	    (void)snprintf(session->subtype, sizeof(session->subtype),
			   "%3.2f", version);
	    mask |= DEVICEID_IS;
	}
	gpsd_report(LOG_DATA,
		    "NDO 0x02: time=%.2f, lat=%.2f lon=%.2f alt=%.2f speed=%.2f track=%.2f climb=%.2f mode=%d subtype='%s' mask=%s\n",
		    session->newdata.time, session->newdata.latitude,
		    session->newdata.longitude, session->newdata.altitude,
		    session->newdata.speed, session->newdata.track,
		    session->newdata.climb, session->newdata.mode,
		    session->gpsdata.dev.subtype, gpsd_maskdump(mask));
	return mask | CLEAR_IS | REPORT_IS;

    case 0x04:			/* DOP Data Output */
	session->context->gps_week = (unsigned short)getleuw(buf2, 2);
	session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
	/*@ ignore @*//*@ splint is confused @ */
	session->newdata.time =
	    gpstime_to_unix(session->context->gps_week,
			    session->context->gps_tow) -
	    session->context->leap_seconds;
	/*@ end @*/
	/*
	 * We make a deliberate choice not to clear DOPs from the
	 * last skyview here, but rather to treat this as a supplement
	 * to our calculations from the visiniolity matrix, trusting
	 * the firmware algorithms over ours.
	 */
	session->gpsdata.dop.gdop = (double)getub(buf2, 8) * 0.1;
	session->gpsdata.dop.pdop = (double)getub(buf2, 9) * 0.1;
	session->gpsdata.dop.hdop = (double)getub(buf2, 10) * 0.1;
	session->gpsdata.dop.vdop = (double)getub(buf2, 11) * 0.1;
	session->gpsdata.dop.tdop = (double)getub(buf2, 12) * 0.1;
	switch (getub(buf2, 13)) {
	case 0:		/* no position fix */
	case 1:		/* manual calls this "1D navigation" */
	    session->gpsdata.status = STATUS_NO_FIX;
	    session->newdata.mode = MODE_NO_FIX;
	    break;
	case 2:		/* 2D navigation */
	    session->gpsdata.status = STATUS_FIX;
	    session->newdata.mode = MODE_2D;
	    break;
	case 3:		/* 3D navigation */
	    session->gpsdata.status = STATUS_FIX;
	    session->newdata.mode = MODE_3D;
	    break;
	case 4:		/* 3D navigation with DGPS */
	    session->gpsdata.status = STATUS_DGPS_FIX;
	    session->newdata.mode = MODE_3D;
	    break;
	}
	/* that's all the information in this packet */
	mask = TIME_IS | DOP_IS | MODE_IS | STATUS_IS;
	gpsd_report(LOG_DATA,
		    "DDO 0x04: gdop=%.2f pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f mode=%d, status=%d mask={TIME| DOP|MODE|STATUS}\n",
		    session->gpsdata.dop.gdop, session->gpsdata.dop.pdop,
		    session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
		    session->gpsdata.dop.tdop, session->newdata.mode,
		    session->gpsdata.status);
	return mask;

    case 0x06:			/* Channel Status Output */
	session->context->gps_week = (unsigned short)getleuw(buf2, 2);
	session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
	/*@ ignore @*//*@ splint is confused @ */
	session->gpsdata.skyview_time =
	    gpstime_to_unix(session->context->gps_week,
			    session->context->gps_tow) -
	    session->context->leap_seconds;
	/*@ end @*/
	session->gpsdata.satellites_visible = (int)getub(buf2, 8);
	gpsd_zero_satellites(&session->gpsdata);
	memset(session->gpsdata.used, 0, sizeof(session->gpsdata.used));
	if (session->gpsdata.satellites_visible > 12) {
	    gpsd_report(LOG_WARN,
			"Warning: EverMore packet has information about %d satellites!\n",
			session->gpsdata.satellites_visible);
	}
	if (session->gpsdata.satellites_visible > EVERMORE_CHANNELS)
	    session->gpsdata.satellites_visible = EVERMORE_CHANNELS;
	satcnt = 0;
	for (i = 0; i < (size_t) session->gpsdata.satellites_visible; i++) {
	    int prn;
	    // channel = getub(buf2, 7*i+7+2)
	    prn = (int)getub(buf2, 7 * i + 7 + 3);
	    if (prn == 0)
		continue;	/* satellite record is not valid */
	    session->gpsdata.PRN[satcnt] = prn;
	    session->gpsdata.azimuth[satcnt] =
		(int)getleuw(buf2, 7 * i + 7 + 4);
	    session->gpsdata.elevation[satcnt] =
		(int)getub(buf2, 7 * i + 7 + 6);
	    session->gpsdata.ss[satcnt] = (float)getub(buf2, 7 * i + 7 + 7);
	    /*
	     * Status bits at offset 8:
	     * bit0 = 1 satellite acquired
	     * bit1 = 1 code-tracking loop locked
	     * bit2 = 1 carrier-tracking loop locked
	     * bit3 = 1 data-bit synchronization done
	     * bit4 = 1 frame synchronization done
	     * bit5 = 1 ephemeris data collected
	     * bit6 = 1 used for position fix
	     */
	    if (getub(buf2, 7 * i + 7 + 8) & 0x40) {
		session->gpsdata.used[session->gpsdata.satellites_used++] =
		    prn;
	    }

	    satcnt++;
	}
	session->gpsdata.satellites_visible = (int)satcnt;
	/* that's all the information in this packet */
	mask = SATELLITE_IS | USED_IS;
	gpsd_report(LOG_DATA,
		    "CSO 0x06: time=%.2f used=%d visible=%d mask={TIME|SATELLITE|USED}\n",
		    session->newdata.time, session->gpsdata.satellites_used,
		    session->gpsdata.satellites_visible);
	return mask;

    case 0x08:			/* Measurement Data Output */
	/* clock offset is a manufacturer diagnostic */
	/* (int)getleuw(buf2, 8);  clock offset, 29000..29850 ?? */
	session->context->gps_week = (unsigned short)getleuw(buf2, 2);
	session->context->gps_tow = (double)getleul(buf2, 4) * 0.01;
	/*@ ignore @*//*@ splint is confused @ */
	session->newdata.time =
	    gpstime_to_unix(session->context->gps_week,
			    session->context->gps_tow) -
	    session->context->leap_seconds;
	/*@ end @*/
	visible = (unsigned char)getub(buf2, 10);
	/*
	 * Note: This code is untested. It was written from the manual.
	 * The results need to be sanity-checked against a GPS with
	 * known-good raw decoding and the same skyview.
	 *
	 * We can get pseudo range (m), delta-range (m/s), doppler (Hz)
	 * and status for each channel from the chip.  We cannot get
	 * codephase or carrierphase.
	 */
#define SBITS(sat, s, l)	sbits((char *)buf, 10 + (sat*14) + s, l)
#define UBITS(sat, s, l)	ubits((char *)buf, 10 + (sat*14) + s, l)
	for (k = 0; k < visible; k++) {
	    int prn = (int)UBITS(k, 4, 5);
	    /* this is so we can tell which never got set */
	    for (j = 0; j < MAXCHANNELS; j++)
		session->gpsdata.raw.mtime[j] = 0;
	    for (j = 0; j < MAXCHANNELS; j++) {
		if (session->gpsdata.PRN[j] == prn) {
		    session->gpsdata.raw.codephase[j] = NAN;
		    session->gpsdata.raw.carrierphase[j] = NAN;
		    session->gpsdata.raw.mtime[j] = session->newdata.time;
		    session->gpsdata.raw.satstat[j] = (unsigned)UBITS(k, 24, 8);
		    session->gpsdata.raw.pseudorange[j] = (double)SBITS(k,40,32);
		    session->gpsdata.raw.deltarange[j] = (double)SBITS(k,72,32);
		    session->gpsdata.raw.doppler[j] = (double)SBITS(k, 104, 16);
		}
	    }
	}
#undef SBITS
#undef UBITS
	gpsd_report(LOG_DATA, "MDO 0x04: time=%.2f mask={TIME|RAW}\n",
		    session->newdata.time);
	return TIME_IS | RAW_IS;

    case 0x20:			/* LogConfig Info, could be used as a probe for EverMore GPS */
	gpsd_report(LOG_IO, "LogConfig EverMore packet, length %zd: %s\n",
		    datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_IO));
	return ONLINE_IS;

    case 0x22:			/* LogData */
	gpsd_report(LOG_IO, "LogData EverMore packet, length %zd: %s\n",
		    datalen, gpsd_hexdump_wrapper(buf2, datalen, LOG_IO));
	return ONLINE_IS;

    case 0x38:			/* ACK */
	gpsd_report(LOG_PROG, "EverMore command %02X ACK\n", getub(buf2, 2));
	return ONLINE_IS;

    default:
	gpsd_report(LOG_WARN,
		    "unknown EverMore packet EID 0x%02x, length %zd: %s\n",
		    buf2[0], datalen, gpsd_hexdump_wrapper(buf2, datalen,
							   LOG_IO));
	return 0;
    }
}
コード例 #2
0
ファイル: driver_ais.c プロジェクト: sensarliar/zfcs_arago_fs
/*@ +charint @*/
bool ais_binary_decode(const struct gpsd_errout_t *errout,
		       struct ais_t *ais,
		       const unsigned char *bits, size_t bitlen,
		       struct ais_type24_queue_t *type24_queue)
/* decode an AIS binary packet */
{
    unsigned int u; int i;

#ifdef S_SPLINT_S
    assert(type24_queue != NULL);
#endif /* S_SPLINT_S */
#define UBITS(s, l)	ubits((unsigned char *)bits, s, l, false)
#define SBITS(s, l)	sbits((signed char *)bits, s, l, false)
#define UCHARS(s, to)	from_sixbit((unsigned char *)bits, s, sizeof(to)-1, to)
#define ENDCHARS(s, to)	from_sixbit((unsigned char *)bits, s, (bitlen-(s))/6,to)
    ais->type = UBITS(0, 6);
    ais->repeat = UBITS(6, 2);
    ais->mmsi = UBITS(8, 30);
    gpsd_log(errout, LOG_INF,
	     "AIVDM message type %d, MMSI %09d:\n",
	     ais->type, ais->mmsi);

#define PERMISSIVE_LENGTH_CHECK(correct) \
	if (bitlen < correct) { \
	    gpsd_log(errout, LOG_ERROR, \
		     "AIVDM message type %d size < %d bits (%zd).\n",	\
		     ais->type, correct, bitlen);			\
	    return false; \
	} else if (bitlen > correct) { \
	    gpsd_log(errout, LOG_WARN, \
		     "AIVDM message type %d size > %d bits (%zd).\n",	\
		     ais->type, correct, bitlen);			\
	}
#define RANGE_CHECK(min, max) \
	if (bitlen < min || bitlen > max) { \
	    gpsd_log(errout, LOG_ERROR, \
		     "AIVDM message type %d size is out of range (%zd).\n", \
		     ais->type, bitlen);				\
	    return false; \
	}

    /*
     * 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:
	PERMISSIVE_LENGTH_CHECK(168)
	ais->type1.status	= UBITS(38, 4);
	ais->type1.turn		= SBITS(42, 8);
	ais->type1.speed	= UBITS(50, 10);
	ais->type1.accuracy	= UBITS(60, 1)!=0;
	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, 19);
	break;
    case 4: 	/* Base Station Report */
    case 11:	/* UTC/Date Response */
	PERMISSIVE_LENGTH_CHECK(168)
	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);
	break;
    case 5: /* Ship static and voyage related data */
	if (bitlen != 424) {
	    gpsd_log(errout, LOG_WARN,
		     "AIVDM message type 5 size not 424 bits (%zd).\n",
		     bitlen);
	    /*
	     * For unknown reasons, a lot of transmitters in the wild ship
	     * with a length of 420 or 422.  This is a recoverable error.
	     */
	    if (bitlen < 420)
		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);
	if (bitlen >= 423)
	    ais->type5.dte          = UBITS(422, 1);
	//ais->type5.spare        = UBITS(423, 1);
	break;
    case 6: /* Addressed Binary Message */
	RANGE_CHECK(88, 1008);
	ais->type6.seqno          = UBITS(38, 2);
	ais->type6.dest_mmsi      = UBITS(40, 30);
	ais->type6.retransmit     = UBITS(70, 1)!=0;
	//ais->type6.spare        = UBITS(71, 1);
	ais->type6.dac            = UBITS(72, 10);
	ais->type6.fid            = UBITS(82, 6);
	ais->type6.bitcount       = bitlen - 88;
	/* not strictly required - helps stability in testing */ 
	(void)memset(ais->type6.bitdata, '\0', sizeof(ais->type6.bitdata));
	ais->type6.structured = false;
	/* Inland AIS */
	if (ais->type6.dac == 200) {
	    switch (ais->type6.fid) {
	    case 21:	/* ETA at lock/bridge/terminal */
		if (bitlen != 248)
		    break;
		UCHARS(88, ais->type6.dac200fid21.country);
		UCHARS(100, ais->type6.dac200fid21.locode);
		UCHARS(118, ais->type6.dac200fid21.section);
		UCHARS(148, ais->type6.dac200fid21.terminal);
		UCHARS(178, ais->type6.dac200fid21.hectometre);
		ais->type6.dac200fid21.month	= UBITS(208, 4);
		ais->type6.dac200fid21.day	= UBITS(212, 5);
		ais->type6.dac200fid21.hour	= UBITS(217, 5);
		ais->type6.dac200fid21.minute	= UBITS(222, 6);
		ais->type6.dac200fid21.tugs	= UBITS(228, 3);
		ais->type6.dac200fid21.airdraught	= UBITS(231, 12);
		/* skip 5 bits */
		ais->type6.structured = true;
		break;
	    case 22:	/* RTA at lock/bridge/terminal */
		if (bitlen != 232)
		    break;
		UCHARS(88, ais->type6.dac200fid22.country);
		UCHARS(100, ais->type6.dac200fid22.locode);
		UCHARS(118, ais->type6.dac200fid22.section);
		UCHARS(148, ais->type6.dac200fid22.terminal);
		UCHARS(178, ais->type6.dac200fid22.hectometre);
		ais->type6.dac200fid22.month	= UBITS(208, 4);
		ais->type6.dac200fid22.day	= UBITS(212, 5);
		ais->type6.dac200fid22.hour	= UBITS(217, 5);
		ais->type6.dac200fid22.minute	= UBITS(222, 6);
		ais->type6.dac200fid22.status	= UBITS(228, 2);
		/* skip 2 bits */
		ais->type6.structured = true;
		break;
	    case 55:	/* Number of Persons On Board */
		if (bitlen != 168)
		    break;
		ais->type6.dac200fid55.crew	= UBITS(88, 8);
		ais->type6.dac200fid55.passengers	= UBITS(96, 13);
		ais->type6.dac200fid55.personnel	= UBITS(109, 8);
		/* skip 51 bits */
		ais->type6.structured = true;
		break;
	    }
	    break;
	}
	/* UK and Republic Of Ireland */
	else if (ais->type6.dac == 235 || ais->type6.dac == 250) {
	    switch (ais->type6.fid) {
	    case 10:	/* GLA - AtoN monitoring data */
		if (bitlen != 136)
		    break;
		ais->type6.dac235fid10.ana_int	= UBITS(88, 10);
		ais->type6.dac235fid10.ana_ext1	= UBITS(98, 10);
		ais->type6.dac235fid10.ana_ext2	= UBITS(108, 10);
		ais->type6.dac235fid10.racon    = UBITS(118, 2);
		ais->type6.dac235fid10.light    = UBITS(120, 2);
		ais->type6.dac235fid10.alarm    = UBITS(122, 1);
		ais->type6.dac235fid10.stat_ext	= UBITS(123, 8);
		ais->type6.dac235fid10.off_pos  = UBITS(131, 1);
		/* skip 4 bits */
		ais->type6.structured = true;
		break;
	    }
	    break;
	}
	/* International */
	else if (ais->type6.dac == 1)
	    switch (ais->type6.fid) {
	    case 12:	/* IMO236 - Dangerous cargo indication */
		UCHARS(88, ais->type6.dac1fid12.lastport);
		ais->type6.dac1fid12.lmonth		= UBITS(118, 4);
		ais->type6.dac1fid12.lday		= UBITS(122, 5);
		ais->type6.dac1fid12.lhour		= UBITS(127, 5);
		ais->type6.dac1fid12.lminute	= UBITS(132, 6);
		UCHARS(138, ais->type6.dac1fid12.nextport);
		ais->type6.dac1fid12.nmonth		= UBITS(168, 4);
		ais->type6.dac1fid12.nday		= UBITS(172, 5);
		ais->type6.dac1fid12.nhour		= UBITS(177, 5);
		ais->type6.dac1fid12.nminute	= UBITS(182, 6);
		UCHARS(188, ais->type6.dac1fid12.dangerous);
		UCHARS(308, ais->type6.dac1fid12.imdcat);
		ais->type6.dac1fid12.unid		= UBITS(332, 13);
		ais->type6.dac1fid12.amount		= UBITS(345, 10);
		ais->type6.dac1fid12.unit		= UBITS(355, 2);
		/* skip 3 bits */
		ais->type6.structured = true;
		break;
	    case 14:	/* IMO236 - Tidal Window */
		ais->type6.dac1fid32.month	= UBITS(88, 4);
		ais->type6.dac1fid32.day	= UBITS(92, 5);
#define ARRAY_BASE 97
#define ELEMENT_SIZE 93
		for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
		    int a = ARRAY_BASE + (ELEMENT_SIZE*u);
		    struct tidal_t *tp = &ais->type6.dac1fid32.tidals[u];
		    tp->lat	= SBITS(a + 0, 27);
		    tp->lon	= SBITS(a + 27, 28);
		    tp->from_hour	= UBITS(a + 55, 5);
		    tp->from_min	= UBITS(a + 60, 6);
		    tp->to_hour	= UBITS(a + 66, 5);
		    tp->to_min	= UBITS(a + 71, 6);
		    tp->cdir	= UBITS(a + 77, 9);
		    tp->cspeed	= UBITS(a + 86, 7);
		}
		ais->type6.dac1fid32.ntidals = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		ais->type6.structured = true;
		break;
	    case 15:	/* IMO236 - Extended Ship Static and Voyage Related Data */
		ais->type6.dac1fid15.airdraught	= UBITS(56, 11);
		ais->type6.structured = true;
		break;
	    case 16:	/* IMO236 - Number of persons on board */
		if (ais->type6.bitcount == 136)
		    ais->type6.dac1fid16.persons = UBITS(88, 13);/* 289 */
		else
		    ais->type6.dac1fid16.persons = UBITS(55, 13);/* 236 */
		ais->type6.structured = true;
		break;
	    case 18:	/* IMO289 - Clearance time to enter port */
		ais->type6.dac1fid18.linkage	= UBITS(88, 10);
		ais->type6.dac1fid18.month	= UBITS(98, 4);
		ais->type6.dac1fid18.day	= UBITS(102, 5);
		ais->type6.dac1fid18.hour	= UBITS(107, 5);
		ais->type6.dac1fid18.minute	= UBITS(112, 6);
		UCHARS(118, ais->type6.dac1fid18.portname);
		UCHARS(238, ais->type6.dac1fid18.destination);
		ais->type6.dac1fid18.lon	= SBITS(268, 25);
		ais->type6.dac1fid18.lat	= SBITS(293, 24);
		/* skip 43 bits */
		ais->type6.structured = true;
		break;
	    case 20:	/* IMO289 - Berthing data - addressed */
		ais->type6.dac1fid20.linkage	= UBITS(88, 10);
		ais->type6.dac1fid20.berth_length	= UBITS(98, 9);
		ais->type6.dac1fid20.berth_depth	= UBITS(107, 8);
		ais->type6.dac1fid20.position	= UBITS(115, 3);
		ais->type6.dac1fid20.month		= UBITS(118, 4);
		ais->type6.dac1fid20.day		= UBITS(122, 5);
		ais->type6.dac1fid20.hour		= UBITS(127, 5);
		ais->type6.dac1fid20.minute		= UBITS(132, 6);
		ais->type6.dac1fid20.availability	= UBITS(138, 1);
		ais->type6.dac1fid20.agent		= UBITS(139, 2);
		ais->type6.dac1fid20.fuel		= UBITS(141, 2);
		ais->type6.dac1fid20.chandler	= UBITS(143, 2);
		ais->type6.dac1fid20.stevedore	= UBITS(145, 2);
		ais->type6.dac1fid20.electrical	= UBITS(147, 2);
		ais->type6.dac1fid20.water		= UBITS(149, 2);
		ais->type6.dac1fid20.customs	= UBITS(151, 2);
		ais->type6.dac1fid20.cartage	= UBITS(153, 2);
		ais->type6.dac1fid20.crane		= UBITS(155, 2);
		ais->type6.dac1fid20.lift		= UBITS(157, 2);
		ais->type6.dac1fid20.medical	= UBITS(159, 2);
		ais->type6.dac1fid20.navrepair	= UBITS(161, 2);
		ais->type6.dac1fid20.provisions	= UBITS(163, 2);
		ais->type6.dac1fid20.shiprepair	= UBITS(165, 2);
		ais->type6.dac1fid20.surveyor	= UBITS(167, 2);
		ais->type6.dac1fid20.steam		= UBITS(169, 2);
		ais->type6.dac1fid20.tugs		= UBITS(171, 2);
		ais->type6.dac1fid20.solidwaste	= UBITS(173, 2);
		ais->type6.dac1fid20.liquidwaste	= UBITS(175, 2);
		ais->type6.dac1fid20.hazardouswaste	= UBITS(177, 2);
		ais->type6.dac1fid20.ballast	= UBITS(179, 2);
		ais->type6.dac1fid20.additional	= UBITS(181, 2);
		ais->type6.dac1fid20.regional1	= UBITS(183, 2);
		ais->type6.dac1fid20.regional2	= UBITS(185, 2);
		ais->type6.dac1fid20.future1	= UBITS(187, 2);
		ais->type6.dac1fid20.future2	= UBITS(189, 2);
		UCHARS(191, ais->type6.dac1fid20.berth_name);
		ais->type6.dac1fid20.berth_lon	= SBITS(311, 25);
		ais->type6.dac1fid20.berth_lat	= SBITS(336, 24);
		ais->type6.structured = true;
		break;
	    case 23:        /* IMO289 - Area notice - addressed */
		break;
	    case 25:	/* IMO289 - Dangerous cargo indication */
		ais->type6.dac1fid25.unit 	= UBITS(88, 2);
		ais->type6.dac1fid25.amount	= UBITS(90, 10);
		for (u = 0; 100 + u*17 < bitlen; u++) {
		    ais->type6.dac1fid25.cargos[u].code    = UBITS(100+u*17,4);
		    ais->type6.dac1fid25.cargos[u].subtype = UBITS(104+u*17,13);
		}
		ais->type6.dac1fid25.ncargos = u;
		ais->type6.structured = true;
		break;
	    case 28:	/* IMO289 - Route info - addressed */
		ais->type6.dac1fid28.linkage	= UBITS(88, 10);
		ais->type6.dac1fid28.sender		= UBITS(98, 3);
		ais->type6.dac1fid28.rtype		= UBITS(101, 5);
		ais->type6.dac1fid28.month		= UBITS(106, 4);
		ais->type6.dac1fid28.day		= UBITS(110, 5);
		ais->type6.dac1fid28.hour		= UBITS(115, 5);
		ais->type6.dac1fid28.minute		= UBITS(120, 6);
		ais->type6.dac1fid28.duration	= UBITS(126, 18);
		ais->type6.dac1fid28.waycount	= UBITS(144, 5);
#define ARRAY_BASE 149
#define ELEMENT_SIZE 55
		for (u = 0; u < (unsigned char)ais->type6.dac1fid28.waycount; u++) {
		    int a = ARRAY_BASE + (ELEMENT_SIZE*u);
		    ais->type6.dac1fid28.waypoints[u].lon = SBITS(a+0, 28);
		    ais->type6.dac1fid28.waypoints[u].lat = SBITS(a+28,27);
		}
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		ais->type6.structured = true;
		break;
	    case 30:	/* IMO289 - Text description - addressed */
		ais->type6.dac1fid30.linkage   = UBITS(88, 10);
		ENDCHARS(98, ais->type6.dac1fid30.text);
		ais->type6.structured = true;
		break;
	    case 32:	/* IMO289 - Tidal Window */
		ais->type6.dac1fid32.month	= UBITS(88, 4);
		ais->type6.dac1fid32.day	= UBITS(92, 5);
#define ARRAY_BASE 97
#define ELEMENT_SIZE 88
		for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
		    int a = ARRAY_BASE + (ELEMENT_SIZE*u);
		    struct tidal_t *tp = &ais->type6.dac1fid32.tidals[u];
		    tp->lon	= SBITS(a + 0, 25);
		    tp->lat	= SBITS(a + 25, 24);
		    tp->from_hour	= UBITS(a + 49, 5);
		    tp->from_min	= UBITS(a + 54, 6);
		    tp->to_hour	= UBITS(a + 60, 5);
		    tp->to_min	= UBITS(a + 65, 6);
		    tp->cdir	= UBITS(a + 71, 9);
		    tp->cspeed	= UBITS(a + 80, 8);
		}
		ais->type6.dac1fid32.ntidals = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		ais->type6.structured = true;
		break;
	    }
	if (!ais->type6.structured)
	    (void)memcpy(ais->type6.bitdata,
			 (char *)bits + (88 / CHAR_BIT),
			 BITS_TO_BYTES(ais->type6.bitcount));
	break;
    case 7: /* Binary acknowledge */
    case 13: /* Safety Related Acknowledge */
    {
	unsigned int mmsi[4];
	RANGE_CHECK(72, 158);
	for (u = 0; u < sizeof(mmsi)/sizeof(mmsi[0]); u++)
	    if (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 @*/
	break;
    }
    case 8: /* Binary Broadcast Message */
	RANGE_CHECK(56, 1008);
	//ais->type8.spare        = UBITS(38, 2);
	ais->type8.dac            = UBITS(40, 10);
	ais->type8.fid            = UBITS(50, 6);
	ais->type8.bitcount       = bitlen - 56;
	/* not strictly required - helps stability in testing */ 
	(void)memset(ais->type8.bitdata, '\0', sizeof(ais->type8.bitdata));
	ais->type8.structured = false;
	if (ais->type8.dac == 1)
	    switch (ais->type8.fid) {
	    case 11:        /* IMO236 - Meteorological/Hydrological data */
		/* layout is almost identical to FID=31 from IMO289 */
		ais->type8.dac1fid11.lat		= SBITS(56, 24);
		ais->type8.dac1fid11.lon		= SBITS(80, 25);
		ais->type8.dac1fid11.day		= UBITS(105, 5);
		ais->type8.dac1fid11.hour		= UBITS(110, 5);
		ais->type8.dac1fid11.minute		= UBITS(115, 6);
		ais->type8.dac1fid11.wspeed		= UBITS(121, 7);
		ais->type8.dac1fid11.wgust		= UBITS(128, 7);
		ais->type8.dac1fid11.wdir		= UBITS(135, 9);
		ais->type8.dac1fid11.wgustdir	= UBITS(144, 9);
		ais->type8.dac1fid11.airtemp	= UBITS(153, 11);
		ais->type8.dac1fid11.humidity	= UBITS(164, 7);
		ais->type8.dac1fid11.dewpoint	= UBITS(171, 10);
		ais->type8.dac1fid11.pressure	= UBITS(181, 9);
		ais->type8.dac1fid11.pressuretend	= UBITS(190, 2);
		ais->type8.dac1fid11.visibility	= UBITS(192, 8);
		ais->type8.dac1fid11.waterlevel	= UBITS(200, 9);
		ais->type8.dac1fid11.leveltrend	= UBITS(209, 2);
		ais->type8.dac1fid11.cspeed		= UBITS(211, 8);
		ais->type8.dac1fid11.cdir		= UBITS(219, 9);
		ais->type8.dac1fid11.cspeed2	= UBITS(228, 8);
		ais->type8.dac1fid11.cdir2		= UBITS(236, 9);
		ais->type8.dac1fid11.cdepth2	= UBITS(245, 5);
		ais->type8.dac1fid11.cspeed3	= UBITS(250, 8);
		ais->type8.dac1fid11.cdir3		= UBITS(258, 9);
		ais->type8.dac1fid11.cdepth3	= UBITS(267, 5);
		ais->type8.dac1fid11.waveheight	= UBITS(272, 8);
		ais->type8.dac1fid11.waveperiod	= UBITS(280, 6);
		ais->type8.dac1fid11.wavedir	= UBITS(286, 9);
		ais->type8.dac1fid11.swellheight	= UBITS(295, 8);
		ais->type8.dac1fid11.swellperiod	= UBITS(303, 6);
		ais->type8.dac1fid11.swelldir	= UBITS(309, 9);
		ais->type8.dac1fid11.seastate	= UBITS(318, 4);
		ais->type8.dac1fid11.watertemp	= UBITS(322, 10);
		ais->type8.dac1fid11.preciptype	= UBITS(332, 3);
		ais->type8.dac1fid11.salinity	= UBITS(335, 9);
		ais->type8.dac1fid11.ice		= UBITS(344, 2);
		ais->type8.structured = true;
		break;
	    case 13:        /* IMO236 - Fairway closed */
		UCHARS(56, ais->type8.dac1fid13.reason);
		UCHARS(176, ais->type8.dac1fid13.closefrom);
		UCHARS(296, ais->type8.dac1fid13.closeto);
		ais->type8.dac1fid13.radius 	= UBITS(416, 10);
		ais->type8.dac1fid13.extunit	= UBITS(426, 2);
		ais->type8.dac1fid13.fday   	= UBITS(428, 5);
		ais->type8.dac1fid13.fmonth 	= UBITS(433, 4);
		ais->type8.dac1fid13.fhour  	= UBITS(437, 5);
		ais->type8.dac1fid13.fminute	= UBITS(442, 6);
		ais->type8.dac1fid13.tday   	= UBITS(448, 5);
		ais->type8.dac1fid13.tmonth 	= UBITS(453, 4);
		ais->type8.dac1fid13.thour  	= UBITS(457, 5);
		ais->type8.dac1fid13.tminute	= UBITS(462, 6);
		/* skip 4 bits */
		ais->type8.structured = true;
		break;
	    case 15:        /* IMO236 - Extended ship and voyage */
		ais->type8.dac1fid15.airdraught	= UBITS(56, 11);
		/* skip 5 bits */
		ais->type8.structured = true;
		break;
	    case 16:	    /* Number of Persons On Board */
		if (ais->type8.bitcount == 136)
		    ais->type8.dac1fid16.persons = UBITS(88, 13);/* 289 */
		else
		    ais->type8.dac1fid16.persons = UBITS(55, 13);/* 236 */
		ais->type8.structured = true;
		break;
	    case 17:        /* IMO289 - VTS-generated/synthetic targets */
#define ARRAY_BASE 56
#define ELEMENT_SIZE 122
		for (u = 0; ARRAY_BASE + (ELEMENT_SIZE*u) <= bitlen; u++) {
		    struct target_t *tp = &ais->type8.dac1fid17.targets[u];
		    int a = ARRAY_BASE + (ELEMENT_SIZE*u);
		    tp->idtype = UBITS(a + 0, 2);
		    switch (tp->idtype) {
		    case DAC1FID17_IDTYPE_MMSI:
			tp->id.mmsi	= UBITS(a + 2, 42);
			break;
		    case DAC1FID17_IDTYPE_IMO:
			tp->id.imo	= UBITS(a + 2, 42);
			break;
		    case DAC1FID17_IDTYPE_CALLSIGN:
			UCHARS(a+2, tp->id.callsign);
			break;
		    default:
			UCHARS(a+2, tp->id.other);
			break;
		    }
		    /* skip 4 bits */
		    tp->lat	= SBITS(a + 48, 24);
		    tp->lon	= SBITS(a + 72, 25);
		    tp->course	= UBITS(a + 97, 9);
		    tp->second	= UBITS(a + 106, 6);
		    tp->speed	= UBITS(a + 112, 10);
		}
		ais->type8.dac1fid17.ntargets = u;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		ais->type8.structured = true;
		break;
	    case 19:        /* IMO289 - Marine Traffic Signal */
		ais->type8.dac1fid19.linkage	= UBITS(56, 10);
		UCHARS(66, ais->type8.dac1fid19.station);
		ais->type8.dac1fid19.lon	= SBITS(186, 25);
		ais->type8.dac1fid19.lat	= SBITS(211, 24);
		ais->type8.dac1fid19.status	= UBITS(235, 2);
		ais->type8.dac1fid19.signal	= UBITS(237, 5);
		ais->type8.dac1fid19.hour	= UBITS(242, 5);
		ais->type8.dac1fid19.minute	= UBITS(247, 6);
		ais->type8.dac1fid19.nextsignal	= UBITS(253, 5);
		/* skip 102 bits */
		ais->type8.structured = true;
		break;
	    case 21:        /* IMO289 - Weather obs. report from ship */
		break;
	    case 22:        /* IMO289 - Area notice - broadcast */
		break;
	    case 24:        /* IMO289 - Extended ship static & voyage-related data */
		break;
	    case 26:        /* IMO289 - Environmental */
		break;
	    case 27:        /* IMO289 - Route information - broadcast */
		ais->type8.dac1fid27.linkage	= UBITS(56, 10);
		ais->type8.dac1fid27.sender	= UBITS(66, 3);
		ais->type8.dac1fid27.rtype	= UBITS(69, 5);
		ais->type8.dac1fid27.month	= UBITS(74, 4);
		ais->type8.dac1fid27.day	= UBITS(78, 5);
		ais->type8.dac1fid27.hour	= UBITS(83, 5);
		ais->type8.dac1fid27.minute	= UBITS(88, 6);
		ais->type8.dac1fid27.duration	= UBITS(94, 18);
		ais->type8.dac1fid27.waycount	= UBITS(112, 5);
#define ARRAY_BASE 117
#define ELEMENT_SIZE 55
		for (i = 0; i < ais->type8.dac1fid27.waycount; i++) {
		    int a = ARRAY_BASE + (ELEMENT_SIZE*i);
		    ais->type8.dac1fid27.waypoints[i].lon	= SBITS(a + 0, 28);
		    ais->type8.dac1fid27.waypoints[i].lat	= SBITS(a + 28, 27);
		}
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		ais->type8.structured = true;
		break;
	    case 29:        /* IMO289 - Text Description - broadcast */
		ais->type8.dac1fid29.linkage   = UBITS(56, 10);
		ENDCHARS(66, ais->type8.dac1fid29.text);
		ais->type8.structured = true;
		break;
	    case 31:        /* IMO289 - 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(136, 9);
		ais->type8.dac1fid31.wgustdir	= UBITS(145, 9);
		ais->type8.dac1fid31.airtemp	= SBITS(154, 11);
		ais->type8.dac1fid31.humidity	= UBITS(165, 7);
		ais->type8.dac1fid31.dewpoint	= SBITS(172, 10);
		ais->type8.dac1fid31.pressure	= UBITS(182, 9);
		ais->type8.dac1fid31.pressuretend	= UBITS(191, 2);
		ais->type8.dac1fid31.visgreater	= UBITS(193, 1);
		ais->type8.dac1fid31.visibility	= UBITS(194, 7);
		ais->type8.dac1fid31.waterlevel	= UBITS(201, 12);
		ais->type8.dac1fid31.leveltrend	= UBITS(213, 2);
		ais->type8.dac1fid31.cspeed		= UBITS(215, 8);
		ais->type8.dac1fid31.cdir		= UBITS(223, 9);
		ais->type8.dac1fid31.cspeed2	= UBITS(232, 8);
		ais->type8.dac1fid31.cdir2		= UBITS(240, 9);
		ais->type8.dac1fid31.cdepth2	= UBITS(249, 5);
		ais->type8.dac1fid31.cspeed3	= UBITS(254, 8);
		ais->type8.dac1fid31.cdir3		= UBITS(262, 9);
		ais->type8.dac1fid31.cdepth3	= UBITS(271, 5);
		ais->type8.dac1fid31.waveheight	= UBITS(276, 8);
		ais->type8.dac1fid31.waveperiod	= UBITS(284, 6);
		ais->type8.dac1fid31.wavedir	= UBITS(290, 9);
		ais->type8.dac1fid31.swellheight	= UBITS(299, 8);
		ais->type8.dac1fid31.swellperiod	= UBITS(307, 6);
		ais->type8.dac1fid31.swelldir	= UBITS(313, 9);
		ais->type8.dac1fid31.seastate	= UBITS(322, 4);
		ais->type8.dac1fid31.watertemp	= SBITS(326, 10);
		ais->type8.dac1fid31.preciptype	= UBITS(336, 3);
		ais->type8.dac1fid31.salinity	= UBITS(339, 9);
		ais->type8.dac1fid31.ice		= UBITS(348, 2);
		ais->type8.structured = true;
		break;
	    }
	else if (ais->type8.dac == 200) {
	    switch (ais->type8.fid) {
	    case 10:	/* Inland ship static and voyage related data */
		if (bitlen != 168)
		    break;
		UCHARS(56, ais->type8.dac200fid10.vin);
		ais->type8.dac200fid10.length	= UBITS(104, 13);
		ais->type8.dac200fid10.beam	= UBITS(117, 10);
		ais->type8.dac200fid10.shiptype	= UBITS(127, 14);
		ais->type8.dac200fid10.hazard	= UBITS(141, 3);
		ais->type8.dac200fid10.draught	= UBITS(144, 11);
		ais->type8.dac200fid10.loaded	= UBITS(155, 2);
		ais->type8.dac200fid10.speed_q	= (bool)UBITS(157, 1);
		ais->type8.dac200fid10.course_q	= (bool)UBITS(158, 1);
		ais->type8.dac200fid10.heading_q	= (bool)UBITS(159, 1);
		/* skip 8 bits */
		/*
		 * Attempt to prevent false matches with this message type 
		 * by range-checking certain fields.
		 */
		if (ais->type8.dac200fid10.hazard > DAC200FID10_HAZARD_MAX
		    || !isascii((int)ais->type8.dac200fid10.vin[0]))
		    ais->type8.structured = false;
		else
		    ais->type8.structured = true;
		break;
	    case 23:	/* EMMA warning */
		if (bitlen != 256)
		    break;
		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.intensity	= UBITS(244, 2);
		ais->type8.dac200fid23.wind	= UBITS(246, 4);
		/* skip 6 bits */
		ais->type8.structured = true;
		break;
	    case 24:	/* Water level */
		if (bitlen != 168)
		    break;
		UCHARS(56, ais->type8.dac200fid24.country);
#define ARRAY_BASE 68
#define ELEMENT_SIZE 25
		for (i = 0; ARRAY_BASE + (ELEMENT_SIZE*i) < (int)bitlen; i++) {
		    int a = ARRAY_BASE + (ELEMENT_SIZE*i);
		    ais->type8.dac200fid24.gauges[i].id = UBITS(a+0,  11);
		    ais->type8.dac200fid24.gauges[i].level = SBITS(a+11, 14);
		}
		ais->type8.dac200fid24.ngauges = i;
#undef ARRAY_BASE
#undef ELEMENT_SIZE
		/* skip 6 bits */
		ais->type8.structured = true;
		break;
	    case 40:	/* Signal status */
		if (bitlen != 168)
		    break;
		ais->type8.dac200fid40.lon	= SBITS(56, 28);
		ais->type8.dac200fid40.lat	= SBITS(84, 27);
		ais->type8.dac200fid40.form	= UBITS(111, 4);
		ais->type8.dac200fid40.facing	= UBITS(115, 9);
		ais->type8.dac200fid40.direction	= UBITS(124, 3);
		ais->type8.dac200fid40.status	= UBITS(127, 30);
		/* skip 11 bits */
		ais->type8.structured = true;
		break;
	    }
	}
	/* land here if we failed to match a known DAC/FID */
	if (!ais->type8.structured)
	    (void)memcpy(ais->type8.bitdata,
			 (char *)bits + (56 / CHAR_BIT),
			 BITS_TO_BYTES(ais->type8.bitcount));
	break;
    case 9: /* Standard SAR Aircraft Position Report */
	PERMISSIVE_LENGTH_CHECK(168);
	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, 20);
	break;
    case 10: /* UTC/Date inquiry */
	PERMISSIVE_LENGTH_CHECK(72);
	//ais->type10.spare        = UBITS(38, 2);
	ais->type10.dest_mmsi      = UBITS(40, 30);
	//ais->type10.spare2       = UBITS(70, 2);
	break;
    case 12: /* Safety Related Message */
	RANGE_CHECK(72, 1008);
	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);
	ENDCHARS(72, ais->type12.text);
	break;
    case 14:	/* Safety Related Broadcast Message */
	RANGE_CHECK(40, 1008);
	//ais->type14.spare          = UBITS(38, 2);
	ENDCHARS(40, ais->type14.text);
	break;
    case 15:	/* Interrogation */
	RANGE_CHECK(88, 168);
	(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 (bitlen > 90) {
	    ais->type15.type1_2	= UBITS(90, 6);
	    ais->type15.offset1_2	= UBITS(96, 12);
	    //ais->type14.spare3    = UBITS(108, 2);
	    if (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);
	    }
	}
	break;
    case 16:	/* Assigned Mode Command */
	RANGE_CHECK(96, 144);
	ais->type16.mmsi1		= UBITS(40, 30);
	ais->type16.offset1		= UBITS(70, 12);
	ais->type16.increment1	= UBITS(82, 10);
	if (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);
	}
	break;
    case 17:	/* GNSS Broadcast Binary Message */
	RANGE_CHECK(80, 816);
	//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        = bitlen - 80;
	(void)memcpy(ais->type17.bitdata,
		     (char *)bits + (80 / CHAR_BIT),
		     BITS_TO_BYTES(ais->type17.bitcount));
	break;
    case 18:	/* Standard Class B CS Position Report */
	PERMISSIVE_LENGTH_CHECK(168)
	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);
	break;
    case 19:	/* Extended Class B CS Position Report */
	PERMISSIVE_LENGTH_CHECK(312)
	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(301, 4);
	ais->type19.raim         = UBITS(305, 1)!=0;
	ais->type19.dte          = UBITS(306, 1)!=0;
	ais->type19.assigned     = UBITS(307, 1)!=0;
	//ais->type19.spare      = UBITS(308, 4);
	break;
    case 20:	/* Data Link Management Message */
	RANGE_CHECK(72, 160);
	//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 */
	RANGE_CHECK(272, 360);
	ais->type21.aid_type = UBITS(38, 5);
	from_sixbit((unsigned char *)bits,
		    43, 20, ais->type21.name);
	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);
	if (strlen(ais->type21.name) == 20 && bitlen > 272)
	    ENDCHARS(272, ais->type21.name+20);
	break;
    case 22:	/* Channel Management */
	PERMISSIVE_LENGTH_CHECK(168)
	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             = UBITS(69, 30);
	    ais->type22.mmsi.dest2             = UBITS(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 */
	PERMISSIVE_LENGTH_CHECK(160)
	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:
	    RANGE_CHECK(160, 168);
	    /* save incoming 24A shipname/MMSI pairs in a circular queue */
	    {
		struct ais_type24a_t *saveptr = &type24_queue->ships[type24_queue->index];

		gpsd_log(errout, LOG_PROG,
			 "AIVDM: 24A from %09u stashed.\n",
			 ais->mmsi);
		saveptr->mmsi = ais->mmsi;
		UCHARS(40, saveptr->shipname);
		++type24_queue->index;
		type24_queue->index %= MAX_TYPE24_INTERLEAVE;
	    }
	    //ais->type24.a.spare	= UBITS(160, 8);

	    UCHARS(40, ais->type24.shipname);
	    ais->type24.part = part_a;
	    return true;
	case 1:
	    PERMISSIVE_LENGTH_CHECK(168)
	    ais->type24.shiptype = UBITS(40, 8);
	    /*
	     * In ITU-R 1371-4, there are new model and serial fields
	     * carved out of the right-hand end of vendorid, which is
	     * reduced from 7 chars to 3.  To cope with older AIS
	     * implementations conforming to revision 3 and older,
	     * unpack the trailing bits *both* ways; truly
	     * revision-4-conformant implementations will have up to
	     * four characters of trailing garbage on the vendorid,
	     * and older implementations will have garbafe in the
	     * model and serial fields.
	     */
	    UCHARS(48, ais->type24.vendorid);
	    ais->type24.model = UBITS(66, 4);
	    ais->type24.serial = UBITS(70, 20);
	    UCHARS(90, ais->type24.callsign);
	    if (AIS_AUXILIARY_MMSI(ais->mmsi)) {
		ais->type24.mothership_mmsi   = UBITS(132, 30);
	    } 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);
	    }
	    //ais->type24.b.spare	    = UBITS(162, 8);

	    /* search the 24A queue for a matching MMSI */
	    for (i = 0; i < MAX_TYPE24_INTERLEAVE; i++) {
		if (type24_queue->ships[i].mmsi == ais->mmsi) {
		    (void)strlcpy(ais->type24.shipname,
				  type24_queue->ships[i].shipname,
				  sizeof(ais->type24.shipname));
		    gpsd_log(errout, LOG_PROG,
			     "AIVDM 24B from %09u matches a 24A.\n",
			     ais->mmsi);
		    /* prevent false match if a 24B is repeated */
		    type24_queue->ships[i].mmsi = 0;
		    ais->type24.part = both;
		    return true;
		}
	    }

	    /* no match, return Part B */
	    ais->type24.part = part_b;
	    return true;
	default:
	    gpsd_log(errout, LOG_WARN,
		     "AIVDM message type 24 of subtype unknown.\n");
	    return false;
	}
	// break;
    case 25:	/* Binary Message, Single Slot */
	/* this check and the following one reject line noise */
	if (bitlen < 40 || bitlen > 168) {
	    gpsd_log(errout, LOG_WARN,
		     "AIVDM message type 25 size not between 40 to 168 bits (%zd).\n",
		     bitlen);
	    return false;
	}
	ais->type25.addressed	= (bool)UBITS(38, 1);
	ais->type25.structured	= (bool)UBITS(39, 1);
	if (bitlen < (unsigned)(40 + (16*ais->type25.structured) + (30*ais->type25.addressed))) {
	    gpsd_log(errout, 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);
	ais->type25.bitcount       = bitlen - 40 - 16*ais->type25.structured;
	/* bit 40 is exactly 5 bytes in; 2 bytes is 16 bits */
	(void)memcpy(ais->type25.bitdata,
		     (char *)bits+5 + 2 * ais->type25.structured,
		     BITS_TO_BYTES(ais->type25.bitcount));
	/* discard MMSI if addressed */
	if (ais->type25.addressed) {
	    shiftleft((unsigned char *)ais->type25.bitdata, ais->type25.bitcount, 30);
	    ais->type25.bitcount -= 30;
	}
	break;
    case 26:	/* Binary Message, Multiple Slot */
	RANGE_CHECK(60, 1004);
	ais->type26.addressed	= (bool)UBITS(38, 1);
	ais->type26.structured	= (bool)UBITS(39, 1);
	if ((signed)bitlen < 40 + 16*ais->type26.structured + 30*ais->type26.addressed + 20) {
	    gpsd_log(errout, 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        = bitlen - 60 - 16*ais->type26.structured;
	(void)memcpy(ais->type26.bitdata,
		     (unsigned char *)bits+5 + 2 * ais->type26.structured,
		     BITS_TO_BYTES(ais->type26.bitcount));
	/* discard MMSI if addressed */
	if (ais->type26.addressed) {
	    shiftleft((unsigned char *)ais->type26.bitdata, ais->type26.bitcount, 30);
	    ais->type26.bitcount -= 30;
	}
	break;
    case 27:	/* Long Range AIS Broadcast message */
	if (bitlen != 96 && bitlen != 168) {
	    gpsd_log(errout, LOG_WARN,
		     "unexpected AIVDM message type 27 (%zd).\n",
		     bitlen);
	    return false;
	} if (bitlen == 168) {
	    /*
	     * This is an implementation error observed in the wild,
	     * sending a full 168-bit slot rather than just 96 bits.
	     */
	    gpsd_log(errout, LOG_WARN,
		     "oversized 169=8-bit AIVDM message type 27.\n");
	}
	ais->type27.accuracy        = (bool)UBITS(38, 1);
	ais->type27.raim		= UBITS(39, 1)!=0;
	ais->type27.status		= UBITS(40, 4);
	ais->type27.lon		= SBITS(44, 18);
	ais->type27.lat		= SBITS(62, 17);
	ais->type27.speed		= UBITS(79, 6);
	ais->type27.course		= UBITS(85, 9);
	ais->type27.gnss            = (bool)UBITS(94, 1);
	break;
    default:
	gpsd_log(errout, LOG_ERROR,
		 "Unparsed AIVDM message type %d.\n",ais->type);
	return false;
    }
    /* *INDENT-ON* */
#undef UCHARS
#undef SBITS
#undef UBITS

    /* data is fully decoded */
    return true;
}