Ais6_1_0::Ais6_1_0(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), ack_required(false), msg_seq(0), spare2(0) { assert(dac == 1); assert(fi == 0); if (num_bits < 88 || num_bits > 936) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; // TODO(schwehr): what is the real max size? const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); ack_required = bs[88]; msg_seq = bs.ToUnsignedInt(89, 11); const size_t text_size = 6 * ((num_bits - 100) / 6); const size_t spare2_size = num_bits - 100 - text_size; text = bs.ToString(100, text_size); if (!spare2_size) spare2 = 0; else spare2 = bs.ToUnsignedInt(100 + text_size, spare2_size); assert(bs.GetRemaining() == 0); status = AIS_OK; }
Ais6::Ais6(const char *nmea_payload, const size_t pad) : AisMsg(nmea_payload, pad), seq(0), mmsi_dest(0), retransmit(false), spare(0), dac(0), fi(0) { assert(message_id == 6); // TODO(olafsinram): 46 or rather 56? const int payload_len = num_bits - 46; // in bits w/o DAC/FI if (num_bits < 88 || payload_len < 0 || payload_len > 952) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(38); seq = bs.ToUnsignedInt(38, 2); mmsi_dest = bs.ToUnsignedInt(40, 30); retransmit = !bs[70]; spare = bs[71]; dac = bs.ToUnsignedInt(72, 10); fi = bs.ToUnsignedInt(82, 6); }
// IFM 4: Capability reply - OLD ITU 1371-4 // TODO(schwehr): WTF? 10 + 128 + 6 == 80 Is this 168 or 232 bits? Ais6_1_4::Ais6_1_4(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), ack_dac(0), capabilities(), cap_reserved(), spare2(0) { assert(dac == 1); assert(fi == 4); // TODO(schwehr): num_bits for 6_1_4. 226 bits? if (num_bits != 232) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); ack_dac = bs.ToUnsignedInt(88, 10); for (size_t cap_num = 0; cap_num < 128/2; cap_num++) { size_t start = 98 + cap_num * 2; capabilities[cap_num] = bs[start]; cap_reserved[cap_num] = bs[start + 1]; } // spare2 = bs.ToUnsignedInt(226, 6); // OR NOT // TODO(schwehr): add in the offset of the dest mmsi assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO 1371-5 Ack Ais6_1_5::Ais6_1_5(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), ack_dac(0), ack_fi(0), seq_num(0), ai_available(false), ai_response(0), spare(0) { assert(dac == 1); assert(fi == 5); if (num_bits != 168) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); ack_dac = bs.ToUnsignedInt(88, 10); ack_fi = bs.ToUnsignedInt(98, 6); seq_num = bs.ToUnsignedInt(104, 11); ai_available = static_cast<bool>(bs[115]); ai_response = bs.ToUnsignedInt(116, 3); spare = bs.ToUnsignedInt(119, 49); assert(bs.GetRemaining() == 0); status = AIS_OK; }
Ais6_1_1::Ais6_1_1(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), ack_dac(0), msg_seq(0), spare2(0) { assert(dac == 1); assert(fi == 1); if (num_bits != 112) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); ack_dac = bs.ToUnsignedInt(88, 10); msg_seq = bs.ToUnsignedInt(98, 11); spare2 = bs.ToUnsignedInt(109, 3); assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO Circ 289 - Dangerous cargo // See also Circ 236 Ais6_1_12::Ais6_1_12(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), utc_month_dep(0), utc_day_dep(0), utc_hour_dep(0), utc_min_dep(0), utc_month_next(0), utc_day_next(0), utc_hour_next(0), utc_min_next(0), un(0), value(0), value_unit(0), spare2(0) { assert(dac == 1); assert(fi == 12); if (num_bits != 360) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } // TODO(schwehr): add in the offset of the dest mmsi #if 0 bs.SeekTo(56); last_port = bs.ToString(56, 30); utc_month_dep = bs.ToUnsignedInt(86, 4); utc_day_dep = bs.ToUnsignedInt(90, 5); utc_hour_dep = bs.ToUnsignedInt(95, 5); utc_min_dep = bs.ToUnsignedInt(100, 6); next_port = bs.ToString(106, 30); utc_month_next = bs.ToUnsignedInt(136, 4); // estimated arrival utc_day_next = bs.ToUnsignedInt(140, 5); utc_hour_next = bs.ToUnsignedInt(145, 5); utc_min_next = bs.ToUnsignedInt(150, 6); main_danger = bs.ToString(156, 120); imo_cat = bs.ToString(276, 24); un = bs.ToUnsignedInt(300, 13); value = bs.ToUnsignedInt(313, 10); // TODO(schwehr): units value_unit = bs.ToUnsignedInt(323, 2); spare = bs.ToUnsignedInt(325, 3); // 360 #endif // TODO(schwehr): Add assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO Circ 289 - Tidal Window // See also Circ 236 Ais6_1_14::Ais6_1_14(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), utc_month(0), utc_day(0) { // TODO(schwehr): untested - no sample of the correct length yet assert(dac == 1); assert(fi == 14); if (num_bits != 376) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); utc_month = bs.ToUnsignedInt(88, 4); utc_day = bs.ToUnsignedInt(92, 5); for (size_t window_num = 0; window_num < 3; window_num++) { Ais6_1_14_Window w; const size_t start = 97 + window_num * 93; // Reversed order for lng/lat. float y = bs.ToInt(start, 27) / 600000.; float x = bs.ToInt(start + 27, 28) / 600000.; w.position = AisPoint(x, y); w.utc_hour_from = bs.ToUnsignedInt(start + 55, 5); w.utc_min_from = bs.ToUnsignedInt(start + 60, 6); w.utc_hour_to = bs.ToUnsignedInt(start + 66, 5); w.utc_min_to = bs.ToUnsignedInt(start + 71, 6); w.cur_dir = bs.ToUnsignedInt(start + 77, 9); w.cur_speed = bs.ToUnsignedInt(start + 86, 7) / 10.; windows.push_back(w); } assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO Circ 289 - Berthing data Ais6_1_20::Ais6_1_20(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), link_id(0), length(0), depth(0.0), mooring_position(0), utc_month(0), utc_day(0), utc_hour(0), utc_min(0), services_known(false), services() { assert(dac == 1); assert(fi == 20); if (num_bits != 360) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); link_id = bs.ToUnsignedInt(88, 10); length = bs.ToUnsignedInt(98, 9); depth = bs.ToUnsignedInt(107, 8); mooring_position = bs.ToUnsignedInt(115, 3); utc_month = bs.ToUnsignedInt(118, 4); utc_day = bs.ToUnsignedInt(122, 5); utc_hour = bs.ToUnsignedInt(127, 5); utc_min = bs.ToUnsignedInt(132, 6); services_known = bs[138]; for (size_t serv_num = 0; serv_num < 26; serv_num++) { // TODO(schwehr): const int val = bs.ToUnsignedInt(139 + 2*serv_num, 2); services[serv_num] = static_cast<int>(bs.ToUnsignedInt(139 + 2*serv_num, 2)); } name = bs.ToString(191, 120); position = bs.ToAisPoint(311, 49); assert(bs.GetRemaining() == 0); status = AIS_OK; }
// http://www.e-navigation.nl/content/monitoring-aids-navigation Ais6_0_0::Ais6_0_0(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), sub_id(1), voltage(0.0), current(0.0), dc_power_supply(true), light_on(true), battery_low(false), off_position(false), spare2(0) { assert(dac == 0); assert(fi == 0); if (num_bits != 136) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); sub_id = bs.ToUnsignedInt(88, 16); voltage = bs.ToUnsignedInt(104, 12) / 10.0; current = bs.ToUnsignedInt(116, 10) / 10.0; dc_power_supply = bs[126]; light_on = bs[127]; battery_low = bs[128]; off_position = bs[129]; spare2 = bs.ToUnsignedInt(130, 6); assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO Circ 289 - Tidal window // See also Circ 236 Ais6_1_32::Ais6_1_32(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), utc_month(0), utc_day(0) { assert(dac == 1); assert(fi == 32); // TODO(schwehr): might get messages with not all windows if (num_bits != 350) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); utc_month = bs.ToUnsignedInt(88, 4); utc_day = bs.ToUnsignedInt(92, 5); for (size_t window_num = 0; window_num < 3; window_num++) { Ais6_1_32_Window w; const size_t start = 97 + 88*window_num; w.position = bs.ToAisPoint(start, 49); w.from_utc_hour = bs.ToUnsignedInt(start + 49, 5); w.from_utc_min = bs.ToUnsignedInt(start + 54, 6); w.to_utc_hour = bs.ToUnsignedInt(start + 60, 5); w.to_utc_min = bs.ToUnsignedInt(start + 65, 6); w.cur_dir = bs.ToUnsignedInt(start + 71, 9); w.cur_speed = bs.ToUnsignedInt(start + 80, 8) / 10.; windows.push_back(w); } assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IFM 40: people on board - OLD ITU 1371-4 Ais6_1_40::Ais6_1_40(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), persons(0), spare2(0) { assert(dac == 1); assert(fi == 40); if (num_bits != 104) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); persons = bs.ToUnsignedInt(88, 13); spare2 = bs.ToUnsignedInt(101, 3); assert(bs.GetRemaining() == 0); status = AIS_OK; }
Ais6_1_2::Ais6_1_2(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), req_dac(0), req_fi(0) { assert(dac == 1); assert(fi == 2); if (num_bits != 104) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); req_dac = bs.ToUnsignedInt(88, 10); req_fi = bs.ToUnsignedInt(98, 6); assert(bs.GetRemaining() == 0); status = AIS_OK; }
Ais23::Ais23(const char *nmea_payload, const size_t pad) : AisMsg(nmea_payload, pad), spare(0), station_type(0), type_and_cargo(0), spare2(3), txrx_mode(0), interval_raw(0), quiet(0), spare3(0) { assert(message_id == 23); if (pad != 2 || num_chars != 27) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(38); spare = bs.ToUnsignedInt(38, 2); position1 = bs.ToAisPoint(40, 35); position2 = bs.ToAisPoint(75, 35); station_type = bs.ToUnsignedInt(110, 4); type_and_cargo = bs.ToUnsignedInt(114, 8); spare2 = bs.ToUnsignedInt(122, 22); txrx_mode = bs.ToUnsignedInt(144, 2); interval_raw = bs.ToUnsignedInt(146, 4); quiet = bs.ToUnsignedInt(150, 4); spare3 = bs.ToUnsignedInt(154, 6); assert(bs.GetRemaining() == 0); status = AIS_OK; }
// IMO Circ 289 - Dangerous cargo indication 2 // See also Circ 236 Ais6_1_25::Ais6_1_25(const char *nmea_payload, const size_t pad) : Ais6(nmea_payload, pad), amount_unit(0), amount(0) { assert(dac == 1); assert(fi == 25); // TODO(schwehr): verify multiple of the size of cargos + header // or padded to a slot boundary // Allowing a message with no payloads // TODO(schwehr): (num_bits - 100) % 17 != 0) is okay if (num_bits < 100 || num_bits > 576) { status = AIS_ERR_BAD_BIT_COUNT; return; } if ((num_bits - 100) % 17 != 0) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(88); amount_unit = bs.ToUnsignedInt(88, 2); amount = bs.ToUnsignedInt(90, 10); const size_t total_cargos = static_cast<int>(floor((num_bits - 100) / 17.)); for (size_t cargo_num = 0; cargo_num < total_cargos; cargo_num++) { Ais6_1_25_Cargo cargo; const size_t start = 100 + 17*cargo_num; cargo.code_type = bs.ToUnsignedInt(start, 4); // TODO(schwehr): Is this the correct behavior? switch (cargo.code_type) { // No 0 case 1: // IMDG Code in packed form cargo.imdg = bs.ToUnsignedInt(start + 4, 7); cargo.imdg_valid = true; cargo.spare = bs.ToUnsignedInt(start + 11, 6); cargo.spare_valid = true; break; case 2: // IGC Code cargo.un = bs.ToUnsignedInt(start + 4, 13); cargo.un_valid = true; break; case 3: // BC Code cargo.bc = bs.ToUnsignedInt(start + 4, 3); cargo.bc_valid = true; cargo.imdg = bs.ToUnsignedInt(start + 7, 7); cargo.imdg_valid = true; cargo.spare = bs.ToUnsignedInt(start + 14, 3); cargo.spare_valid = true; break; case 4: // MARPOL Annex I cargo.marpol_oil = bs.ToUnsignedInt(start + 4, 4); cargo.marpol_oil_valid = true; cargo.spare = bs.ToUnsignedInt(start + 8, 9); cargo.spare_valid = true; break; case 5: // MARPOL Annex II IBC cargo.marpol_cat = bs.ToUnsignedInt(start + 4, 3); cargo.marpol_cat_valid = true; cargo.spare = bs.ToUnsignedInt(start + 7, 10); cargo.spare_valid = true; break; // 6: Regional use // 7: 7-15 reserved for future default: break; // Just push in an all blank record? } cargos.push_back(cargo); } assert(bs.GetRemaining() == 0); status = AIS_OK; }
Ais18::Ais18(const char *nmea_payload, const size_t pad) : AisMsg(nmea_payload, pad), spare(0), sog(0.0), position_accuracy(0), cog(0.0), true_heading(0), timestamp(0), spare2(0), unit_flag(0), display_flag(0), dsc_flag(0), band_flag(0), m22_flag(0), mode_flag(0), raim(false), commstate_flag(0), sync_state(0), slot_timeout_valid(false), slot_timeout(0), received_stations_valid(false), received_stations(0), slot_number_valid(false), slot_number(0), utc_valid(false), utc_hour(0), utc_min(0), utc_spare(0), slot_offset_valid(false), slot_offset(0), slot_increment_valid(false), slot_increment(0), slots_to_allocate_valid(false), slots_to_allocate(0), keep_flag_valid(false), keep_flag(0), commstate_cs_fill_valid(false), commstate_cs_fill(0) { assert(message_id == 18); if (pad != 0 || num_chars != 28) { status = AIS_ERR_BAD_BIT_COUNT; return; } AisBitset bs; const AIS_STATUS r = bs.ParseNmeaPayload(nmea_payload, pad); if (r != AIS_OK) { status = r; return; } bs.SeekTo(38); spare = bs.ToUnsignedInt(38, 8); sog = bs.ToUnsignedInt(46, 10) / 10.; position_accuracy = bs[56]; position = bs.ToAisPoint(57, 55); cog = bs.ToUnsignedInt(112, 12) / 10.; true_heading = bs.ToUnsignedInt(124, 9); timestamp = bs.ToUnsignedInt(133, 6); spare2 = bs.ToUnsignedInt(139, 2); unit_flag = bs[141]; display_flag = bs[142]; dsc_flag = bs[143]; band_flag = bs[144]; m22_flag = bs[145]; mode_flag = bs[146]; raim = bs[147]; commstate_flag = bs[148]; // 0 SOTDMA, 1 ITDMA if (unit_flag == 0) { sync_state = bs.ToUnsignedInt(149, 2); if (commstate_flag == 0) { // SOTDMA slot_timeout = bs.ToUnsignedInt(151, 3); slot_timeout_valid = true; switch (slot_timeout) { case 0: slot_offset = bs.ToUnsignedInt(154, 14); slot_offset_valid = true; break; case 1: utc_hour = bs.ToUnsignedInt(154, 5); utc_min = bs.ToUnsignedInt(159, 7); utc_spare = bs.ToUnsignedInt(166, 2); utc_valid = true; break; case 2: // FALLTHROUGH case 4: // FALLTHROUGH case 6: slot_number = bs.ToUnsignedInt(154, 14); slot_number_valid = true; break; case 3: // FALLTHROUGH case 5: // FALLTHROUGH case 7: received_stations = bs.ToUnsignedInt(154, 14); received_stations_valid = true; break; default: assert(false); } } else { // ITDMA slot_increment = bs.ToUnsignedInt(151, 13); slot_increment_valid = true; slots_to_allocate = bs.ToUnsignedInt(164, 3); slots_to_allocate_valid = true; keep_flag = bs[167]; keep_flag_valid = true; } } else { // Carrier Sense (CS) with unit_flag of 1. commstate_cs_fill = bs.ToUnsignedInt(149, 19); commstate_cs_fill_valid = true; } assert(bs.GetRemaining() == 0); status = AIS_OK; }