src/libais/ais_py.cpp
// TODO(schwehr): check for reference counting leaks
// TODO(schwehr): better error handling for all messages
#include <string>
#include <Python.h>
#include "ais.h"
namespace libais {
PyObject *ais_py_exception;
const char exception_short[] = "DecodeError";
const char exception_name[] = "_ais.DecodeError";
// TODO(schwehr): Write a full module doc string.
// Functional Identifiers (FI) are individual messages within a
// specific DAC. An FI in one DAC has nothing to do with an FI in
// another DAC.
enum AIS_FI {
AIS_FI_6_1_0_TEXT = 0,
AIS_FI_6_1_1_ACK = 1,
AIS_FI_6_1_2_FI_INTERROGATE = 2,
AIS_FI_6_1_3_CAPABILITY_INTERROGATE = 3,
AIS_FI_6_1_4_CAPABILITY_REPLY = 4,
AIS_FI_6_1_5_ACK = 5,
AIS_FI_6_1_12_DANGEROUS_CARGO = 12,
AIS_FI_6_1_14_TIDAL_WINDOW = 14,
AIS_FI_6_1_16_VTS_TARGET = 16,
AIS_FI_6_1_18_ENTRY_TIME = 18,
AIS_FI_6_1_20_BERTHING = 20,
AIS_FI_6_1_25_DANGEROUS_CARGO = 25,
AIS_FI_6_1_28_ROUTE = 28,
AIS_FI_6_1_30_TEXT = 30,
AIS_FI_6_1_32_TIDAL_WINDOW = 32,
AIS_FI_6_1_40_PERSONS_ON_BOARD = 40,
AIS_FI_6_200_21_RIS_VTS_ETA = 21,
AIS_FI_6_200_22_RIS_VTS_RTA = 22,
AIS_FI_6_200_55_RIS_VTS_SAR = 55,
AIS_FI_8_1_0_TEXT = 0,
AIS_FI_8_1_11_MET_HYDRO = 11,
AIS_FI_8_1_13_FAIRWAY_CLOSED = 13,
AIS_FI_8_1_15_SHIP_AND_VOYAGE = 15,
AIS_FI_8_1_16_PERSONS_ON_BOARD = 16,
AIS_FI_8_1_17_VTS_TARGET = 17,
AIS_FI_8_1_19_TRAFFIC_SIGNAL = 19,
AIS_FI_8_1_21_WEATHER_OBS = 21,
AIS_FI_8_1_22_AREA_NOTICE = 22,
AIS_FI_8_1_24_SHIP_AND_VOYAGE = 24,
AIS_FI_8_1_26_SENSOR = 26,
AIS_FI_8_1_27_ROUTE = 27,
AIS_FI_8_1_29_TEXT = 29,
AIS_FI_8_1_31_MET_HYDRO = 31,
AIS_FI_8_1_40_PERSONS_ON_BOARD = 40,
AIS_FI_8_200_10_RIS_SHIP_AND_VOYAGE = 10,
AIS_FI_8_200_21_RIS_ETA_AT_LOCK_BRIDGE_TERMINAL = 21,
AIS_FI_8_200_22_RIS_RTA_AT_LOCK_BRIDGE_TERMINAL = 22,
AIS_FI_8_200_23_RIS_EMMA_WARNING = 23,
AIS_FI_8_200_24_RIS_WATERLEVEL = 24,
AIS_FI_8_200_40_RIS_ATON_SIGNAL_STATUS = 40,
AIS_FI_8_200_55_RIS_PERSONS_ON_BOARD = 50,
AIS_FI_8_366_22_AREA_NOTICE = 22, // USCG.
AIS_FI_8_367_22_AREA_NOTICE = 22, // USCG.
AIS_FI_8_367_23_SSW = 23, // USCG Satellite Ship Weather
AIS_FI_8_367_24_SSW_SMALL = 24, // USCG Satellite Ship Weather Small
AIS_FI_8_367_25_SSW_TINY = 25, // USCG Satellite Ship Weather Tiny
AIS_FI_8_367_33_ENVIRONMENTAL = 33, // Environmental Message
};
void
DictSafeSetItem(
PyObject *dict, const std::string &key, const long val) { // NOLINT
PyObject *val_obj = PyLong_FromLong(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
void
DictSafeSetItem(PyObject *dict, const std::string &key, const int val) {
PyObject *val_obj = PyLong_FromLong(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
void
DictSafeSetItem(
PyObject *dict, const std::string &key, const unsigned int val) {
PyObject *val_obj = PyLong_FromLong(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
void
DictSafeSetItem(PyObject *dict, const std::string &key, const string &val) {
PyObject *val_obj = PyUnicode_FromString(val.c_str());
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
void
DictSafeSetItem(PyObject *dict, const std::string &key, const char *val) {
PyObject *val_obj = PyUnicode_FromString(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
#if 0
void
DictSafeSetItem(PyObject *dict, const std::string &key, const bool val) {
PyObject *key_obj = PyUnicode_FromString(key.c_str());
PyObject *val_obj = PyBool_FromLong(val);
assert(key_obj);
assert(val_obj);
PyDict_SetItem(dict, key_obj, val_obj);
Py_DECREF(key_obj);
Py_DECREF(val_obj);
}
#else
void
DictSafeSetItem(PyObject *dict, const std::string &key, const bool val) {
if (val) {
PyDict_SetItemString(dict, key.c_str(), Py_True);
} else {
PyDict_SetItemString(dict, key.c_str(), Py_False);
}
}
#endif
void
DictSafeSetItem(PyObject *dict, const std::string &key, const float val) {
PyObject *val_obj = PyFloat_FromDouble(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
// Python Floats are IEE-754 double precision.
void
DictSafeSetItem(PyObject *dict, const std::string &key, const double val) {
PyObject *val_obj = PyFloat_FromDouble(val);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
Py_DECREF(val_obj);
}
void
DictSafeSetItem(PyObject *dict, const std::string &x_key, const string& y_key,
const AisPoint& position) {
DictSafeSetItem(dict, x_key, position.lng_deg);
DictSafeSetItem(dict, y_key, position.lat_deg);
}
void
DictSafeSetItem(PyObject *dict, const std::string &key, PyObject *val_obj) {
// When we need to add dictionaries and such to a dictionary
assert(dict);
assert(val_obj);
PyDict_SetItemString(dict, key.c_str(), val_obj);
}
PyObject *
ais_msg_to_pydict(const AisMsg* msg) {
assert(msg);
PyObject *dict = PyDict_New();
DictSafeSetItem(dict, "id", msg->message_id);
DictSafeSetItem(dict, "repeat_indicator", msg->repeat_indicator);
DictSafeSetItem(dict, "mmsi", msg->mmsi);
return dict;
}
// Class A position report
PyObject *
ais1_2_3_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais1_2_3 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais1_2_3: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "nav_status", msg.nav_status);
DictSafeSetItem(dict, "rot_over_range", msg.rot_over_range);
DictSafeSetItem(dict, "rot", msg.rot);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "true_heading", msg.true_heading);
DictSafeSetItem(dict, "timestamp", msg.timestamp);
DictSafeSetItem(dict, "special_manoeuvre", msg.special_manoeuvre);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "raim", msg.raim);
// COMM States
DictSafeSetItem(dict, "sync_state", msg.sync_state); // Both SOTDMA & ITDMA
// SOTDMA
if (msg.message_id == 1 || msg.message_id == 2) {
if (msg.slot_timeout_valid) {
DictSafeSetItem(dict, "slot_timeout", msg.slot_timeout);
}
if (msg.received_stations_valid) {
DictSafeSetItem(dict, "received_stations", msg.received_stations);
}
if (msg.slot_number_valid) {
DictSafeSetItem(dict, "slot_number", msg.slot_number);
}
if (msg.utc_valid) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "utc_spare", msg.utc_spare);
}
if (msg.slot_offset_valid) {
DictSafeSetItem(dict, "slot_offset", msg.slot_offset);
}
}
// ITDMA
if (msg.slot_increment_valid) {
DictSafeSetItem(dict, "slot_increment", msg.slot_increment);
DictSafeSetItem(dict, "slots_to_allocate", msg.slots_to_allocate);
DictSafeSetItem(dict, "keep_flag", msg.keep_flag);
}
return dict;
}
// Basestation report and ';' time report
PyObject *
ais4_11_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais4_11 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais4_11: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "year", msg.year);
DictSafeSetItem(dict, "month", msg.month);
DictSafeSetItem(dict, "day", msg.day);
DictSafeSetItem(dict, "hour", msg.hour);
DictSafeSetItem(dict, "minute", msg.minute);
DictSafeSetItem(dict, "second", msg.second);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "fix_type", msg.fix_type);
DictSafeSetItem(dict, "transmission_ctl", msg.transmission_ctl);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "raim", msg.raim);
// SOTDMA
DictSafeSetItem(dict, "sync_state", msg.sync_state);
DictSafeSetItem(dict, "slot_timeout", msg.slot_timeout);
if (msg.received_stations_valid)
DictSafeSetItem(dict, "received_stations", msg.received_stations);
if (msg.slot_number_valid)
DictSafeSetItem(dict, "slot_number", msg.slot_number);
if (msg.utc_valid) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "utc_spare", msg.utc_spare);
}
if (msg.slot_offset_valid)
DictSafeSetItem(dict, "slot_offset", msg.slot_offset);
return dict;
}
// Class A ship data
PyObject *
ais5_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais5 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais5: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "ais_version", msg.ais_version);
DictSafeSetItem(dict, "imo_num", msg.imo_num);
DictSafeSetItem(dict, "callsign", msg.callsign);
DictSafeSetItem(dict, "name", msg.name);
DictSafeSetItem(dict, "type_and_cargo", msg.type_and_cargo);
DictSafeSetItem(dict, "dim_a", msg.dim_a);
DictSafeSetItem(dict, "dim_b", msg.dim_b);
DictSafeSetItem(dict, "dim_c", msg.dim_c);
DictSafeSetItem(dict, "dim_d", msg.dim_d);
DictSafeSetItem(dict, "fix_type", msg.fix_type);
DictSafeSetItem(dict, "eta_month", msg.eta_month);
DictSafeSetItem(dict, "eta_day", msg.eta_day);
DictSafeSetItem(dict, "eta_hour", msg.eta_hour);
DictSafeSetItem(dict, "eta_minute", msg.eta_minute);
DictSafeSetItem(dict, "draught", msg.draught);
DictSafeSetItem(dict, "destination", msg.destination);
DictSafeSetItem(dict, "dte", msg.dte);
DictSafeSetItem(dict, "spare", msg.spare);
return dict;
}
// Address binary message
AIS_STATUS
ais6_1_0_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_0 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "ack_required", msg.ack_required);
DictSafeSetItem(dict, "msg_seq", msg.msg_seq);
DictSafeSetItem(dict, "text", msg.text);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
AIS_STATUS
ais6_1_1_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_1 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "ack_dac", msg.ack_dac);
DictSafeSetItem(dict, "msg_seq", msg.msg_seq);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
AIS_STATUS
ais6_1_2_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_2 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "req_dac", msg.req_dac);
DictSafeSetItem(dict, "req_fi", msg.req_fi);
return AIS_OK;
}
AIS_STATUS
ais6_1_3_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_3 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "req_dac", msg.req_dac);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "spare3", msg.spare3);
DictSafeSetItem(dict, "spare4", msg.spare4);
return AIS_OK;
}
AIS_STATUS
ais6_1_4_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_4 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "ack_dac", msg.ack_dac);
constexpr int kNumFI = 64;
PyObject *cap_list = PyList_New(kNumFI);
PyObject *res_list = PyList_New(kNumFI);
for (size_t cap_num = 0; cap_num < kNumFI; cap_num++) {
PyObject *cap = PyLong_FromLong(long(msg.capabilities[cap_num])); // NOLINT
PyList_SetItem(cap_list, cap_num, cap);
PyObject *res = PyLong_FromLong(long(msg.cap_reserved[cap_num])); // NOLINT
PyList_SetItem(res_list, cap_num, res);
}
DictSafeSetItem(dict, "capabilities", cap_list);
Py_DECREF(cap_list);
DictSafeSetItem(dict, "cap_reserved", res_list);
Py_DECREF(res_list);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "spare3", msg.spare2);
DictSafeSetItem(dict, "spare4", msg.spare2);
DictSafeSetItem(dict, "spare5", msg.spare2);
return AIS_OK;
}
AIS_STATUS
ais6_1_5_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_5 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "ack_dac", msg.ack_dac);
DictSafeSetItem(dict, "ack_fi", msg.ack_dac);
DictSafeSetItem(dict, "seq_num", msg.ack_dac);
DictSafeSetItem(dict, "ai_available", msg.ack_dac);
DictSafeSetItem(dict, "ai_response", msg.ack_dac);
DictSafeSetItem(dict, "spare", msg.ack_dac);
return AIS_OK;
}
AIS_STATUS
ais6_1_12_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais6_1_12 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "last_port", msg.last_port);
DictSafeSetItem(dict, "utc_month_dep", msg.utc_month_dep); // actual
DictSafeSetItem(dict, "utc_day_dep", msg.utc_day_dep);
DictSafeSetItem(dict, "utc_hour_dep", msg.utc_hour_dep);
DictSafeSetItem(dict, "utc_min_dep", msg.utc_min_dep);
DictSafeSetItem(dict, "next_port", msg.next_port);
DictSafeSetItem(dict, "utc_month_next", msg.utc_month_next); // estimated
DictSafeSetItem(dict, "utc_day_next", msg.utc_day_next);
DictSafeSetItem(dict, "utc_hour_next", msg.utc_hour_next);
DictSafeSetItem(dict, "utc_min_next", msg.utc_min_next);
DictSafeSetItem(dict, "main_danger", msg.main_danger);
DictSafeSetItem(dict, "imo_cat", msg.imo_cat);
DictSafeSetItem(dict, "un", msg.un);
DictSafeSetItem(dict, "value", msg.value); // TODO(schwehr): units?
DictSafeSetItem(dict, "value_unit", msg.value_unit);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// 6_1_13 does not exist
// IMO Circ 289 - Tidal Window
AIS_STATUS
ais6_1_14_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais6_1_14 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
if (!msg.windows.size())
return AIS_ERR_BAD_SUB_MSG;
PyObject *window_list = PyList_New(msg.windows.size());
for (size_t w_num = 0; w_num < msg.windows.size(); w_num++) {
PyObject *window = PyDict_New();
DictSafeSetItem(window, "x", "y", msg.windows[w_num].position);
DictSafeSetItem(window, "utc_hour_from", msg.windows[w_num].utc_hour_from);
DictSafeSetItem(window, "utc_min_from", msg.windows[w_num].utc_min_from);
DictSafeSetItem(window, "utc_hour_to", msg.windows[w_num].utc_hour_to);
DictSafeSetItem(window, "utc_min_to", msg.windows[w_num].utc_min_to);
DictSafeSetItem(window, "cur_dir", msg.windows[w_num].cur_dir);
DictSafeSetItem(window, "cur_speed", msg.windows[w_num].cur_speed);
PyList_SetItem(window_list, w_num, window);
}
PyDict_SetItemString(dict, "windows", window_list);
Py_DECREF(window_list);
return AIS_OK;
}
// 6_1_15, 6_1_16, and 6_1_17 do not exist
// IMO Circ 289 - Clearance time to enter port
AIS_STATUS
ais6_1_18_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais6_1_18 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "port_berth", msg.port_berth);
DictSafeSetItem(dict, "dest", msg.dest);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "spare2_0", msg.spare2[0]);
DictSafeSetItem(dict, "spare2_1", msg.spare2[1]);
return AIS_OK;
}
// 6_1_19 does not exist
// IMO Circ 289 - Berthing data
AIS_STATUS
ais6_1_20_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais6_1_20 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "length", msg.length);
DictSafeSetItem(dict, "depth", msg.depth);
DictSafeSetItem(dict, "position", msg.mooring_position);
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
if (msg.services_known) {
PyObject *serv_list = PyList_New(26);
for (size_t serv_num = 0; serv_num < 26; serv_num++) {
PyObject *serv = PyLong_FromLong(long(msg.services[serv_num])); // NOLINT
PyList_SetItem(serv_list, serv_num, serv);
}
DictSafeSetItem(dict, "services", serv_list);
Py_DECREF(serv_list);
}
DictSafeSetItem(dict, "name", msg.name);
DictSafeSetItem(dict, "x", "y", msg.position);
return AIS_OK;
}
// 6_1_21, 6_1_22, 6_1_23 and 6_1_24 Do not exist (yet?)
AIS_STATUS
ais6_1_25_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_25 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "amount_unit", msg.amount_unit);
DictSafeSetItem(dict, "amount", msg.amount);
if (0 == msg.cargos.size())
return AIS_ERR_BAD_SUB_MSG;
PyObject *cargo_list = PyList_New(msg.cargos.size());
for (size_t cargo_num = 0; cargo_num < msg.cargos.size(); cargo_num++) {
PyObject *cargo = PyDict_New();
if (msg.cargos[cargo_num].imdg_valid)
DictSafeSetItem(cargo, "imdg", msg.cargos[cargo_num].imdg);
if (msg.cargos[cargo_num].spare_valid)
DictSafeSetItem(cargo, "spare", msg.cargos[cargo_num].spare);
if (msg.cargos[cargo_num].un_valid)
DictSafeSetItem(cargo, "un", msg.cargos[cargo_num].un);
if (msg.cargos[cargo_num].bc_valid)
DictSafeSetItem(cargo, "bc", msg.cargos[cargo_num].bc);
if (msg.cargos[cargo_num].marpol_oil_valid)
DictSafeSetItem(cargo, "marpol_oil", msg.cargos[cargo_num].marpol_oil);
if (msg.cargos[cargo_num].marpol_cat_valid)
DictSafeSetItem(cargo, "marpol_cat", msg.cargos[cargo_num].marpol_cat);
PyList_SetItem(cargo_list, cargo_num, cargo);
}
PyDict_SetItemString(dict, "cargos", cargo_list);
Py_DECREF(cargo_list);
return AIS_OK;
}
// 6_1_26
// 6_1_27
// 6_1_28 - TODO(schwehr): Route Addressed - clone from 8_1_27
// 6_1_29
// 6_1_30 - TODO(schwehr): Text Addressed
// 6_1_31
AIS_STATUS
ais6_1_32_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_32 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
PyObject *window_list = PyList_New(msg.windows.size());
for (size_t win_num = 0; win_num < msg.windows.size(); win_num++) {
PyObject *win = PyDict_New();
DictSafeSetItem(win, "x", "y", msg.windows[win_num].position);
DictSafeSetItem(win, "from_utc_hour", msg.windows[win_num].from_utc_hour);
DictSafeSetItem(win, "from_utc_min", msg.windows[win_num].from_utc_min);
DictSafeSetItem(win, "to_utc_hour", msg.windows[win_num].to_utc_hour);
DictSafeSetItem(win, "to_utc_min", msg.windows[win_num].to_utc_min);
DictSafeSetItem(win, "cur_dir", msg.windows[win_num].cur_dir);
DictSafeSetItem(win, "cur_speed", msg.windows[win_num].cur_speed);
PyList_SetItem(window_list, win_num, win);
}
PyDict_SetItemString(dict, "windows", window_list);
Py_DECREF(window_list);
return AIS_OK;
}
AIS_STATUS
ais6_1_40_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_1_40 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "persons", msg.persons);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
PyObject*
ais6_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais6 msg(nmea_payload, pad);
if (msg.had_error() && msg.get_error() != AIS_UNINITIALIZED) {
PyErr_Format(ais_py_exception, "Ais6: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "seq", msg.seq);
DictSafeSetItem(dict, "mmsi_dest", msg.mmsi_dest);
DictSafeSetItem(dict, "retransmit", msg.retransmit);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "dac", msg.dac);
DictSafeSetItem(dict, "fi", msg.fi);
// TODO(schwehr): manage all the submessage types
AIS_STATUS status = AIS_UNINITIALIZED;
switch (msg.dac) {
case AIS_DAC_1_INTERNATIONAL: // IMO.
switch (msg.fi) {
case AIS_FI_6_1_0_TEXT: // OLD ITU 1371-1.
status = ais6_1_0_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_1_ACK: // OLD ITU 1371-1.
status = ais6_1_1_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_2_FI_INTERROGATE: // OLD ITU 1371-1.
status = ais6_1_2_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_3_CAPABILITY_INTERROGATE: // OLD ITU 1371-1.
status = ais6_1_3_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_4_CAPABILITY_REPLY: // OLD ITU 1371-1.
status = ais6_1_4_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_5_ACK: // ITU 1371-5.
status = ais6_1_5_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_12_DANGEROUS_CARGO: // Not to be used after 1 Jan 2013.
status = ais6_1_12_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_14_TIDAL_WINDOW: // Not to be used after 1 Jan 2013.
status = ais6_1_14_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_18_ENTRY_TIME:
status = ais6_1_18_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_20_BERTHING:
status = ais6_1_20_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_25_DANGEROUS_CARGO:
status = ais6_1_25_append_pydict(nmea_payload, dict, pad);
break;
// TODO(schwehr): AIS_FI_6_1_28_ROUTE.
// TODO(schwehr): AIS_FI_6_1_30_TEXT.
case AIS_FI_6_1_32_TIDAL_WINDOW: // IMO Circ 289
status = ais6_1_32_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_6_1_40_PERSONS_ON_BOARD: // OLD ITU 1371-1.
status = ais6_1_40_append_pydict(nmea_payload, dict, pad);
break;
default:
// TODO(schwehr): Raise an exception?
DictSafeSetItem(dict, "not_parsed", true);
}
break;
default:
// TODO(schwehr): Raise an exception?
DictSafeSetItem(dict, "not_parsed", true);
}
if (status != AIS_OK) {
Py_DECREF(dict);
PyErr_Format(ais_py_exception,
"Ais6: DAC:FI not known. 6:%d:%d %s",
msg.dac,
msg.fi,
AIS_STATUS_STRINGS[status]);
return nullptr;
}
return dict;
}
// Acknowledgement
PyObject*
ais7_13_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais7_13 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais7_13: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
PyObject *list = PyList_New(msg.dest_mmsi.size());
for (size_t i = 0; i < msg.dest_mmsi.size(); i++) {
PyObject *tuple = PyTuple_New(2);
PyTuple_SetItem(tuple, 0, PyLong_FromLong(msg.dest_mmsi[i])); // Steals ref
PyTuple_SetItem(tuple, 1, PyLong_FromLong(msg.seq_num[i])); // Steals ref
PyList_SetItem(list, i, tuple); // Steals ref
}
PyDict_SetItemString(dict, "acks", list);
Py_DECREF(list);
return dict;
}
AIS_STATUS
ais8_1_0_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_0 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "ack_required", msg.ack_required);
DictSafeSetItem(dict, "msg_seq", msg.msg_seq);
DictSafeSetItem(dict, "text", msg.text);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// ais 8_1_[1..10] do not exist
AIS_STATUS
ais8_1_11_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_11 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "wind_ave", msg.wind_ave);
DictSafeSetItem(dict, "wind_gust", msg.wind_gust);
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
DictSafeSetItem(dict, "wind_gust_dir", msg.wind_gust);
DictSafeSetItem(dict, "air_temp", msg.air_temp);
DictSafeSetItem(dict, "rel_humid", msg.rel_humid);
DictSafeSetItem(dict, "dew_point", msg.dew_point);
DictSafeSetItem(dict, "air_pres", msg.air_pres);
DictSafeSetItem(dict, "air_pres_trend", msg.air_pres_trend);
DictSafeSetItem(dict, "horz_vis", msg.horz_vis);
DictSafeSetItem(dict, "water_level", msg.water_level);
DictSafeSetItem(dict, "water_level_trend", msg.water_level_trend);
DictSafeSetItem(dict, "surf_cur_speed", msg.surf_cur_speed);
DictSafeSetItem(dict, "surf_cur_dir", msg.surf_cur_dir);
DictSafeSetItem(dict, "cur_speed_2", msg.cur_speed_2);
DictSafeSetItem(dict, "cur_dir_2", msg.cur_dir_2);
DictSafeSetItem(dict, "cur_depth_2", msg.cur_depth_2);
DictSafeSetItem(dict, "cur_speed_3", msg.cur_speed_3);
DictSafeSetItem(dict, "cur_dir_3", msg.cur_dir_3);
DictSafeSetItem(dict, "cur_depth_3", msg.cur_depth_3);
DictSafeSetItem(dict, "wave_height", msg.wave_height);
DictSafeSetItem(dict, "wave_period", msg.wave_period);
DictSafeSetItem(dict, "wave_dir", msg.wave_dir);
DictSafeSetItem(dict, "swell_height", msg.swell_height);
DictSafeSetItem(dict, "swell_period", msg.swell_period);
DictSafeSetItem(dict, "swell_dir", msg.swell_dir);
DictSafeSetItem(dict, "sea_state", msg.sea_state);
DictSafeSetItem(dict, "water_temp", msg.water_temp);
DictSafeSetItem(dict, "precip_type", msg.precip_type);
DictSafeSetItem(dict, "ice", msg.ice); // Grr... ice
// Or could be spare
DictSafeSetItem(dict, "ext_water_level", msg.extended_water_level);
DictSafeSetItem(dict, "spare2", msg.extended_water_level);
return AIS_OK;
}
// 12 is addressed
AIS_STATUS
ais8_1_13_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_13 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "reason", msg.reason);
DictSafeSetItem(dict, "location_from", msg.location_from);
DictSafeSetItem(dict, "location_to", msg.location_to);
DictSafeSetItem(dict, "radius", msg.radius);
DictSafeSetItem(dict, "units", msg.units);
DictSafeSetItem(dict, "day_from", msg.day_from);
DictSafeSetItem(dict, "month_from", msg.month_from);
DictSafeSetItem(dict, "hour_from", msg.hour_from);
DictSafeSetItem(dict, "minute_from", msg.minute_from);
DictSafeSetItem(dict, "day_to", msg.day_to);
DictSafeSetItem(dict, "month_to", msg.month_to);
DictSafeSetItem(dict, "hour_to", msg.hour_to);
DictSafeSetItem(dict, "minute_to", msg.minute_to);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// 14 is addressed
AIS_STATUS
ais8_1_15_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_15 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "air_draught", msg.air_draught);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
AIS_STATUS
ais8_1_16_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_16 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "persons", msg.persons);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
AIS_STATUS
ais8_1_17_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_17 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
PyObject *target_list = PyList_New(msg.targets.size());
for (size_t target_num = 0; target_num < msg.targets.size(); target_num++) {
PyObject *target = PyDict_New();
DictSafeSetItem(target, "type", msg.targets[target_num].type);
DictSafeSetItem(target, "id", msg.targets[target_num].id);
DictSafeSetItem(target, "spare", msg.targets[target_num].spare);
DictSafeSetItem(target, "x", "y", msg.targets[target_num].position);
DictSafeSetItem(target, "cog", msg.targets[target_num].cog);
DictSafeSetItem(target, "timestamp", msg.targets[target_num].timestamp);
DictSafeSetItem(target, "sog", msg.targets[target_num].sog);
PyList_SetItem(target_list, target_num, target);
}
PyDict_SetItemString(dict, "targets", target_list);
Py_DECREF(target_list);
return AIS_OK;
}
// 18 is addressed only
AIS_STATUS
ais8_1_19_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_19 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "name", msg.name);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "status", msg.status);
DictSafeSetItem(dict, "signal", msg.signal);
DictSafeSetItem(dict, "utc_hour_next", msg.utc_hour_next);
DictSafeSetItem(dict, "utc_min_next", msg.utc_min_next);
DictSafeSetItem(dict, "next_signal", msg.next_signal);
DictSafeSetItem(dict, "spare2_0", msg.spare2[0]);
DictSafeSetItem(dict, "spare2_1", msg.spare2[1]);
DictSafeSetItem(dict, "spare2_2", msg.spare2[2]);
DictSafeSetItem(dict, "spare2_3", msg.spare2[3]);
return AIS_OK;
}
// 20 is addressed
AIS_STATUS
ais8_1_21_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_21 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
if (0 == msg.type_wx_report) {
// WX obs from ship
DictSafeSetItem(dict, "location", msg.location);
DictSafeSetItem(dict, "wx", msg.wx[0]); // TODO(schwehr) Rename present?
DictSafeSetItem(dict, "horz_viz", msg.horz_viz);
DictSafeSetItem(dict, "humidity", msg.humidity);
DictSafeSetItem(dict, "wind_speed", msg.wind_speed);
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
DictSafeSetItem(dict, "pressure", msg.pressure);
DictSafeSetItem(dict, "pressure_tendency", msg.pressure_tendency);
DictSafeSetItem(dict, "air_temp", msg.air_temp);
DictSafeSetItem(dict, "water_temp", msg.water_temp);
DictSafeSetItem(dict, "wave_period", msg.wave_period);
DictSafeSetItem(dict, "wave_height", msg.wave_height);
DictSafeSetItem(dict, "wave_dir", msg.wave_dir);
DictSafeSetItem(dict, "swell_height", msg.swell_height);
DictSafeSetItem(dict, "swell_dir", msg.swell_dir);
DictSafeSetItem(dict, "swell_period", msg.swell_period);
DictSafeSetItem(dict, "spare2", msg.spare2);
} else {
// type == 1
// WMO OBS from ship
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "heading", msg.heading);
DictSafeSetItem(dict, "pressure", msg.pressure);
DictSafeSetItem(dict, "rel_pressure", msg.rel_pressure);
DictSafeSetItem(dict, "pressure_tendency", msg.pressure_tendency);
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
DictSafeSetItem(dict, "wind_speed_ms", msg.wind_speed_ms);
DictSafeSetItem(dict, "wind_dir_rel", msg.wind_dir_rel);
DictSafeSetItem(dict, "wind_speed_rel", msg.wind_speed_rel);
DictSafeSetItem(dict, "wind_gust_speed", msg.wind_gust_speed);
DictSafeSetItem(dict, "wind_gust_dir", msg.wind_gust_dir);
DictSafeSetItem(dict, "air_temp_raw", msg.air_temp_raw);
DictSafeSetItem(dict, "humidity", msg.humidity);
DictSafeSetItem(dict, "water_temp_raw", msg.water_temp_raw);
DictSafeSetItem(dict, "horz_viz", msg.horz_viz);
// TODO(schwehr): list?
DictSafeSetItem(dict, "wx", msg.wx[0]);
DictSafeSetItem(dict, "wx_next1", msg.wx[1]);
DictSafeSetItem(dict, "wx_next2", msg.wx[2]);
DictSafeSetItem(dict, "cloud_total", msg.cloud_total);
DictSafeSetItem(dict, "cloud_low", msg.cloud_low);
DictSafeSetItem(dict, "cloud_low_type", msg.cloud_low_type);
DictSafeSetItem(dict, "cloud_middle_type", msg.cloud_middle_type);
DictSafeSetItem(dict, "cloud_high_type", msg.cloud_high_type);
DictSafeSetItem(dict, "alt_lowest_cloud_base", msg.alt_lowest_cloud_base);
DictSafeSetItem(dict, "wave_period", msg.wave_period);
DictSafeSetItem(dict, "wave_height", msg.wave_height);
DictSafeSetItem(dict, "swell_dir", msg.swell_dir);
DictSafeSetItem(dict, "swell_period", msg.swell_period);
DictSafeSetItem(dict, "swell_height", msg.swell_height);
DictSafeSetItem(dict, "swell_dir_2", msg.swell_dir_2);
DictSafeSetItem(dict, "swell_period_2", msg.swell_period_2);
DictSafeSetItem(dict, "swell_height_2", msg.swell_height_2);
DictSafeSetItem(dict, "ice_thickness", msg.ice_thickness);
DictSafeSetItem(dict, "ice_accretion", msg.ice_accretion);
DictSafeSetItem(dict, "ice_accretion_cause", msg.ice_accretion_cause);
DictSafeSetItem(dict, "sea_ice_concentration", msg.sea_ice_concentration);
DictSafeSetItem(dict, "amt_type_ice", msg.amt_type_ice);
DictSafeSetItem(dict, "ice_situation", msg.ice_situation);
DictSafeSetItem(dict, "ice_devel", msg.ice_devel);
DictSafeSetItem(dict, "bearing_ice_edge", msg.bearing_ice_edge);
}
return AIS_OK;
}
AIS_STATUS
ais8_1_22_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
Ais8_1_22 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "notice_type", msg.notice_type);
DictSafeSetItem(dict, "notice_type_str",
ais8_1_22_notice_names[msg.notice_type]);
DictSafeSetItem(dict, "month", msg.month);
DictSafeSetItem(dict, "day", msg.day);
DictSafeSetItem(dict, "hour", msg.hour);
DictSafeSetItem(dict, "minute", msg.minute);
DictSafeSetItem(dict, "duration_minutes", msg.minute);
PyObject *sub_area_list = PyList_New(msg.sub_areas.size());
// Loop over sub_areas
for (size_t i = 0; i < msg.sub_areas.size(); i++) {
switch (msg.sub_areas[i]->getType()) {
case AIS8_1_22_SHAPE_CIRCLE: // or point
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Circle *c =
dynamic_cast<Ais8_1_22_Circle*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_CIRCLE);
if (c->radius_m == 0)
DictSafeSetItem(sub_area, "sub_area_type_str", "point");
else
DictSafeSetItem(sub_area, "sub_area_type_str", "circle");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "radius", c->radius_m);
// TODO(schwehr): spare?
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_1_22_SHAPE_RECT:
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Rect *c =
dynamic_cast<Ais8_1_22_Rect*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_RECT);
DictSafeSetItem(sub_area, "sub_area_type_str", "rect");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "e_dim_m", c->e_dim_m);
DictSafeSetItem(sub_area, "n_dim_m", c->n_dim_m);
DictSafeSetItem(sub_area, "orient_deg", c->orient_deg);
// TODO(schwehr): spare?
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_1_22_SHAPE_SECTOR:
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Sector *c =
dynamic_cast<Ais8_1_22_Sector*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_SECTOR);
DictSafeSetItem(sub_area, "sub_area_type_str", "sector");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "radius", c->radius_m);
DictSafeSetItem(sub_area, "left_bound_deg", c->left_bound_deg);
DictSafeSetItem(sub_area, "right_bound_deg", c->right_bound_deg);
// TODO(schwehr): spare?
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_1_22_SHAPE_POLYLINE:
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Polyline *polyline =
dynamic_cast<Ais8_1_22_Polyline*>(msg.sub_areas[i].get());
assert(polyline != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_POLYLINE);
DictSafeSetItem(sub_area, "sub_area_type_str", "polyline");
assert(polyline->angles.size() == polyline->dists_m.size());
PyObject *angle_list = PyList_New(polyline->angles.size());
PyObject *dist_list = PyList_New(polyline->angles.size());
for (size_t pt_num = 0; pt_num < polyline->angles.size(); pt_num++) {
PyList_SetItem(angle_list, pt_num,
PyFloat_FromDouble(polyline->angles[pt_num]));
PyList_SetItem(dist_list, pt_num,
PyFloat_FromDouble(polyline->dists_m[pt_num]));
}
DictSafeSetItem(sub_area, "angles", angle_list);
DictSafeSetItem(sub_area, "dists_m", dist_list);
Py_DECREF(angle_list);
Py_DECREF(dist_list);
// TODO(schwehr): spare?
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_1_22_SHAPE_POLYGON:
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Polygon *polygon =
dynamic_cast<Ais8_1_22_Polygon*>(msg.sub_areas[i].get());
assert(polygon != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_POLYGON);
DictSafeSetItem(sub_area, "sub_area_type_str", "polygon");
assert(polygon->angles.size() == polygon->dists_m.size());
PyObject *angle_list = PyList_New(polygon->angles.size());
PyObject *dist_list = PyList_New(polygon->angles.size());
for (size_t pt_num = 0; pt_num < polygon->angles.size(); pt_num++) {
PyList_SetItem(angle_list, pt_num,
PyFloat_FromDouble(polygon->angles[pt_num]));
PyList_SetItem(dist_list, pt_num,
PyFloat_FromDouble(polygon->dists_m[pt_num]));
}
DictSafeSetItem(sub_area, "angles", angle_list);
DictSafeSetItem(sub_area, "dists_m", dist_list);
Py_DECREF(angle_list);
Py_DECREF(dist_list);
// TODO(schwehr): spare?
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_1_22_SHAPE_TEXT:
{
PyObject *sub_area = PyDict_New();
Ais8_1_22_Text *text =
dynamic_cast<Ais8_1_22_Text*>(msg.sub_areas[i].get());
assert(text != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_1_22_SHAPE_TEXT);
DictSafeSetItem(sub_area, "sub_area_type_str", "text");
DictSafeSetItem(sub_area, "text", text->text);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
default:
{} // TODO(schwehr): Mark an unknown subarea or raise an exception.
}
}
DictSafeSetItem(dict, "sub_areas", sub_area_list);
Py_DECREF(sub_area_list);
return AIS_OK;
}
// No 23 broadcast
// IMO Circ 289 - Extended ship static and voyage-related
AIS_STATUS
ais8_1_24_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_24 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "air_draught", msg.air_draught);
DictSafeSetItem(dict, "last_port", msg.last_port);
PyObject *port_list = PyList_New(2);
PyList_SetItem(port_list, 0, PyUnicode_FromString(msg.next_ports[0].c_str()));
PyList_SetItem(port_list, 1, PyUnicode_FromString(msg.next_ports[0].c_str()));
PyObject *solas_list = PyList_New(26);
for (size_t solas_num = 0; solas_num < 26; solas_num++) {
PyObject *solas = PyLong_FromLong(msg.solas_status[solas_num]);
PyList_SetItem(solas_list, solas_num, solas);
}
DictSafeSetItem(dict, "port_list", port_list);
Py_DECREF(port_list);
DictSafeSetItem(dict, "solas", solas_list);
Py_DECREF(solas_list);
DictSafeSetItem(dict, "ice_class", msg.ice_class);
DictSafeSetItem(dict, "shaft_power", msg.shaft_power);
DictSafeSetItem(dict, "vhf", msg.vhf);
DictSafeSetItem(dict, "lloyds_ship_type", msg.lloyds_ship_type);
DictSafeSetItem(dict, "gross_tonnage", msg.gross_tonnage);
DictSafeSetItem(dict, "laden_ballast", msg.laden_ballast);
DictSafeSetItem(dict, "heavy_oil", msg.heavy_oil);
DictSafeSetItem(dict, "light_oil", msg.light_oil);
DictSafeSetItem(dict, "diesel", msg.diesel);
DictSafeSetItem(dict, "bunker_oil", msg.bunker_oil);
DictSafeSetItem(dict, "persons", msg.persons);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// No 25 broadcast
AIS_STATUS
ais8_1_26_append_pydict_sensor_hdr(PyObject *dict,
Ais8_1_26_SensorReport* rpt) {
assert(dict);
assert(rpt);
DictSafeSetItem(dict, "report_type", rpt->report_type);
DictSafeSetItem(dict, "utc_day", rpt->utc_day);
DictSafeSetItem(dict, "utc_hr", rpt->utc_hr);
DictSafeSetItem(dict, "utc_min", rpt->utc_min);
DictSafeSetItem(dict, "site_id", rpt->site_id);
return AIS_OK;
}
// IMO Circ 289 - Environmental
AIS_STATUS
ais8_1_26_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_26 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
PyObject *rpt_list = PyList_New(msg.reports.size());
DictSafeSetItem(dict, "reports", rpt_list);
for (size_t rpt_num = 0; rpt_num < msg.reports.size(); rpt_num++) {
PyObject *rpt_dict = PyDict_New();
PyList_SetItem(rpt_list, rpt_num, rpt_dict);
switch (msg.reports[rpt_num]->report_type) {
// case AIS8_1_26_SENSOR_ERROR:
case AIS8_1_26_SENSOR_LOCATION:
{
Ais8_1_26_Location *rpt =
reinterpret_cast<Ais8_1_26_Location *>(msg.reports[rpt_num]);
ais8_1_26_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "x", "y", rpt->position);
DictSafeSetItem(rpt_dict, "z", rpt->z);
DictSafeSetItem(rpt_dict, "owner", rpt->owner);
DictSafeSetItem(rpt_dict, "timeout", rpt->timeout);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_STATION:
{
Ais8_1_26_Station *rpt =
reinterpret_cast<Ais8_1_26_Station *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "name", rpt->name);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_WIND:
{
Ais8_1_26_Wind *rpt =
reinterpret_cast<Ais8_1_26_Wind *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "wind_speed", rpt->wind_speed);
DictSafeSetItem(rpt_dict, "wind_gust", rpt->wind_gust);
DictSafeSetItem(rpt_dict, "wind_dir", rpt->wind_dir);
DictSafeSetItem(rpt_dict, "wind_gust_dir", rpt->wind_gust_dir);
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
DictSafeSetItem(rpt_dict, "wind_forecast", rpt->wind_forecast);
DictSafeSetItem(
rpt_dict, "wind_gust_forecast", rpt->wind_gust_forecast);
DictSafeSetItem(rpt_dict, "wind_dir_forecast", rpt->wind_dir_forecast);
DictSafeSetItem(rpt_dict, "utc_day_forecast", rpt->utc_day_forecast);
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
DictSafeSetItem(rpt_dict, "duration", rpt->duration);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_WATER_LEVEL:
{
Ais8_1_26_WaterLevel *rpt =
reinterpret_cast<Ais8_1_26_WaterLevel *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "type", rpt->type);
DictSafeSetItem(rpt_dict, "level", rpt->level);
DictSafeSetItem(rpt_dict, "trend", rpt->trend);
DictSafeSetItem(rpt_dict, "vdatum", rpt->vdatum);
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
DictSafeSetItem(rpt_dict, "forecast_type", rpt->forecast_type);
DictSafeSetItem(rpt_dict, "level_forecast", rpt->level_forecast);
DictSafeSetItem(rpt_dict, "utc_day_forecast", rpt->utc_day_forecast);
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
DictSafeSetItem(rpt_dict, "duration", rpt->duration);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_CURR_2D:
{
Ais8_1_26_Curr2D *rpt =
reinterpret_cast<Ais8_1_26_Curr2D *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "type", rpt->type);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
PyObject *curr_list = PyList_New(3);
DictSafeSetItem(dict, "currents", curr_list);
for (size_t idx = 0; idx < 3; idx++) {
PyObject *curr_dict = PyDict_New();
DictSafeSetItem(curr_dict, "speed", rpt->currents[idx].speed);
DictSafeSetItem(curr_dict, "dir", rpt->currents[idx].dir);
DictSafeSetItem(curr_dict, "depth", rpt->currents[idx].depth);
PyList_SetItem(curr_list, idx, curr_dict);
}
Py_DECREF(curr_list);
}
break;
case AIS8_1_26_SENSOR_CURR_3D:
{
Ais8_1_26_Curr3D *rpt =
reinterpret_cast<Ais8_1_26_Curr3D *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "type", rpt->type);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
PyObject *curr_list = PyList_New(3);
DictSafeSetItem(dict, "currents", curr_list);
for (size_t idx = 0; idx < 2; idx++) {
// ERROR: no way to specify negative direction
PyObject *curr_dict = PyDict_New();
PyList_SetItem(curr_list, idx, curr_dict);
DictSafeSetItem(curr_dict, "north", rpt->currents[idx].north);
DictSafeSetItem(curr_dict, "east", rpt->currents[idx].east);
DictSafeSetItem(curr_dict, "up", rpt->currents[idx].up);
DictSafeSetItem(curr_dict, "depth", rpt->currents[idx].depth);
}
Py_DECREF(curr_list);
}
break;
case AIS8_1_26_SENSOR_HORZ_FLOW:
{
Ais8_1_26_HorzFlow *rpt =
reinterpret_cast<Ais8_1_26_HorzFlow *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
PyObject *curr_list = PyList_New(3);
DictSafeSetItem(dict, "currents", curr_list);
for (size_t idx = 0; idx < 2; idx++) {
PyObject *curr_dict = PyDict_New();
PyList_SetItem(curr_list, idx, curr_dict);
DictSafeSetItem(curr_dict, "bearing", rpt->currents[idx].bearing);
DictSafeSetItem(curr_dict, "dist", rpt->currents[idx].dist);
DictSafeSetItem(curr_dict, "speed", rpt->currents[idx].speed);
DictSafeSetItem(curr_dict, "dir", rpt->currents[idx].dir);
DictSafeSetItem(curr_dict, "level", rpt->currents[idx].level);
}
Py_DECREF(curr_list);
}
break;
case AIS8_1_26_SENSOR_SEA_STATE:
{
Ais8_1_26_SeaState *rpt =
reinterpret_cast<Ais8_1_26_SeaState *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "swell_height", rpt->swell_height);
DictSafeSetItem(rpt_dict, "swell_period", rpt->swell_period);
DictSafeSetItem(rpt_dict, "swell_dir", rpt->swell_dir);
DictSafeSetItem(rpt_dict, "sea_state", rpt->sea_state);
DictSafeSetItem(rpt_dict, "swell_sensor_type", rpt->swell_sensor_type);
DictSafeSetItem(rpt_dict, "water_temp", rpt->water_temp);
DictSafeSetItem(rpt_dict, "water_temp_depth", rpt->water_temp_depth);
DictSafeSetItem(rpt_dict, "water_sensor_type", rpt->water_sensor_type);
DictSafeSetItem(rpt_dict, "wave_height", rpt->wave_height);
DictSafeSetItem(rpt_dict, "wave_period", rpt->wave_period);
DictSafeSetItem(rpt_dict, "wave_dir", rpt->wave_dir);
DictSafeSetItem(rpt_dict, "wave_sensor_type", rpt->wave_sensor_type);
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
}
break;
case AIS8_1_26_SENSOR_SALINITY:
{
Ais8_1_26_Salinity *rpt =
reinterpret_cast<Ais8_1_26_Salinity *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "water_temp", rpt->water_temp);
DictSafeSetItem(rpt_dict, "conductivity", rpt->conductivity);
DictSafeSetItem(rpt_dict, "pressure", rpt->pressure);
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
DictSafeSetItem(rpt_dict, "salinity_type", rpt->salinity_type);
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
DictSafeSetItem(rpt_dict, "spare0", rpt->spare[0]);
DictSafeSetItem(rpt_dict, "spare1", rpt->spare[1]);
}
break;
case AIS8_1_26_SENSOR_WX:
{
Ais8_1_26_Wx *rpt =
reinterpret_cast<Ais8_1_26_Wx *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "air_temp", rpt->air_temp);
DictSafeSetItem(rpt_dict, "air_temp_sensor_type",
rpt->air_temp_sensor_type);
DictSafeSetItem(rpt_dict, "precip", rpt->precip);
DictSafeSetItem(rpt_dict, "horz_vis", rpt->horz_vis);
DictSafeSetItem(rpt_dict, "dew_point", rpt->dew_point);
DictSafeSetItem(rpt_dict, "dew_point_type", rpt->dew_point_type);
DictSafeSetItem(rpt_dict, "air_pressure", rpt->air_pressure);
DictSafeSetItem(rpt_dict, "air_pressure_trend",
rpt->air_pressure_trend);
DictSafeSetItem(rpt_dict, "air_pressor_type", rpt->air_pressor_type);
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_AIR_DRAUGHT:
{
Ais8_1_26_AirDraught *rpt =
reinterpret_cast<Ais8_1_26_AirDraught *>(msg.reports[rpt_num]);
DictSafeSetItem(rpt_dict, "draught", rpt->draught);
DictSafeSetItem(rpt_dict, "gap", rpt->gap);
DictSafeSetItem(rpt_dict, "forecast_gap", rpt->forecast_gap);
DictSafeSetItem(rpt_dict, "int trend", rpt->trend);
DictSafeSetItem(
rpt_dict, "int utc_day_forecast", rpt->utc_day_forecast);
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
DictSafeSetItem(rpt_dict, "spare", rpt->spare);
}
break;
case AIS8_1_26_SENSOR_RESERVED_11: // FALLTHROUGH
case AIS8_1_26_SENSOR_RESERVED_12:
case AIS8_1_26_SENSOR_RESERVED_13:
case AIS8_1_26_SENSOR_RESERVED_14:
case AIS8_1_26_SENSOR_RESERVED_15:
default:
{} // TODO(schwehr): mark a bad sensor type or raise exception
}
}
Py_DECREF(rpt_list);
return AIS_OK;
}
// IMO Circ 289 - Route information
AIS_STATUS
ais8_1_27_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_27 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "sender_type", msg.sender_type);
DictSafeSetItem(dict, "route_type", msg.route_type);
DictSafeSetItem(dict, "utc_month", msg.utc_month);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "duration", msg.duration);
PyObject *waypoint_list = PyList_New(msg.waypoints.size());
for (size_t point_num = 0; point_num < msg.waypoints.size(); point_num++) {
PyObject *waypoint = PyList_New(2);
// TODO(schwehr): Py_DECREF(); ?
PyList_SetItem(
waypoint, 0, PyFloat_FromDouble(msg.waypoints[point_num].lng_deg));
PyList_SetItem(
waypoint, 1, PyFloat_FromDouble(msg.waypoints[point_num].lat_deg));
PyList_SetItem(waypoint_list, point_num, waypoint);
}
PyDict_SetItemString(dict, "waypoints", waypoint_list);
Py_DECREF(waypoint_list);
return AIS_OK;
}
// no 28 broadcast
// IMO Circ 289 - Text description
AIS_STATUS
ais8_1_29_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_29 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "text", msg.text);
return AIS_OK;
}
// no 30 broadcast
AIS_STATUS
ais8_1_31_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_1_31 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "utc_day", msg.utc_day);
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "wind_ave", msg.wind_ave);
DictSafeSetItem(dict, "wind_gust", msg.wind_gust);
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
DictSafeSetItem(dict, "wind_gust_dir", msg.wind_gust_dir);
DictSafeSetItem(dict, "air_temp", msg.air_temp);
DictSafeSetItem(dict, "rel_humid", msg.rel_humid);
DictSafeSetItem(dict, "dew_point", msg.dew_point);
DictSafeSetItem(dict, "air_pres", msg.air_pres);
DictSafeSetItem(dict, "air_pres_trend", msg.air_pres_trend);
DictSafeSetItem(dict, "horz_vis", msg.horz_vis);
DictSafeSetItem(dict, "water_level", msg.water_level);
DictSafeSetItem(dict, "water_level_trend", msg.water_level_trend);
// TODO(schwehr): make this a list of dicts
DictSafeSetItem(dict, "surf_cur_speed", msg.surf_cur_speed);
DictSafeSetItem(dict, "surf_cur_dir", msg.surf_cur_dir);
DictSafeSetItem(dict, "cur_speed_2", msg.cur_speed_2);
DictSafeSetItem(dict, "cur_dir_2", msg.cur_dir_2);
DictSafeSetItem(dict, "cur_depth_2", msg.cur_depth_2);
DictSafeSetItem(dict, "cur_speed_3", msg.cur_speed_3);
DictSafeSetItem(dict, "cur_dir_3", msg.cur_dir_3);
DictSafeSetItem(dict, "cur_depth_3", msg.cur_depth_3);
DictSafeSetItem(dict, "wave_height", msg.wave_height);
DictSafeSetItem(dict, "wave_period", msg.wave_period);
DictSafeSetItem(dict, "wave_dir", msg.wave_dir);
DictSafeSetItem(dict, "swell_height", msg.swell_height);
DictSafeSetItem(dict, "swell_period", msg.swell_period);
DictSafeSetItem(dict, "swell_dir", msg.swell_dir);
DictSafeSetItem(dict, "sea_state", msg.sea_state);
DictSafeSetItem(dict, "water_temp", msg.water_temp);
DictSafeSetItem(dict, "precip_type", msg.precip_type);
DictSafeSetItem(dict, "salinity", msg.salinity);
DictSafeSetItem(dict, "ice", msg.ice);
return AIS_OK;
}
// no 32 broadcast
// DAC 200 - River Information System
// Inland ship static and voyage related data
AIS_STATUS
ais8_200_10_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_10 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "eu_id", msg.eu_id);
DictSafeSetItem(dict, "length", msg.length);
DictSafeSetItem(dict, "beam", msg.beam);
DictSafeSetItem(dict, "ship_type", msg.ship_type);
DictSafeSetItem(dict, "haz_cargo", msg.haz_cargo);
DictSafeSetItem(dict, "draught", msg.draught);
DictSafeSetItem(dict, "loaded", msg.loaded);
DictSafeSetItem(dict, "speed_qual", msg.speed_qual);
DictSafeSetItem(dict, "course_qual", msg.course_qual);
DictSafeSetItem(dict, "heading_qual", msg.heading_qual);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// River Information System
// ETA report
AIS_STATUS
ais8_200_21_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_21 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "country", msg.country);
DictSafeSetItem(dict, "location", msg.location);
DictSafeSetItem(dict, "section", msg.section);
DictSafeSetItem(dict, "terminal", msg.terminal);
DictSafeSetItem(dict, "hectometre", msg.hectometre);
DictSafeSetItem(dict, "eta_month", msg.eta_month);
DictSafeSetItem(dict, "eta_day", msg.eta_day);
DictSafeSetItem(dict, "eta_hour", msg.eta_hour);
DictSafeSetItem(dict, "eta_minute", msg.eta_minute);
DictSafeSetItem(dict, "tugboats", msg.tugboats);
DictSafeSetItem(dict, "air_draught", msg.air_draught);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// River Information System
// RTA report
AIS_STATUS
ais8_200_22_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_22 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "country", msg.country);
DictSafeSetItem(dict, "location", msg.location);
DictSafeSetItem(dict, "section", msg.section);
DictSafeSetItem(dict, "terminal", msg.terminal);
DictSafeSetItem(dict, "hectometre", msg.hectometre);
DictSafeSetItem(dict, "rta_month", msg.rta_month);
DictSafeSetItem(dict, "rta_day", msg.rta_day);
DictSafeSetItem(dict, "rta_hour", msg.rta_hour);
DictSafeSetItem(dict, "rta_minute", msg.rta_minute);
DictSafeSetItem(dict, "lock_status", msg.lock_status);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// River Information System
// EMMA warning
AIS_STATUS
ais8_200_23_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_23 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "utc_year_start", msg.utc_year_start);
DictSafeSetItem(dict, "utc_month_start", msg.utc_month_start);
DictSafeSetItem(dict, "utc_day_start", msg.utc_day_start);
DictSafeSetItem(dict, "utc_year_end", msg.utc_year_end);
DictSafeSetItem(dict, "utc_month_end", msg.utc_month_end);
DictSafeSetItem(dict, "utc_day_end", msg.utc_day_end);
DictSafeSetItem(dict, "utc_hour_start", msg.utc_hour_start);
DictSafeSetItem(dict, "utc_min_start", msg.utc_min_start);
DictSafeSetItem(dict, "utc_hour_end", msg.utc_hour_end);
DictSafeSetItem(dict, "utc_min_end", msg.utc_min_end);
DictSafeSetItem(dict, "x1", "y1", msg.position1);
DictSafeSetItem(dict, "x2", "y2", msg.position2);
DictSafeSetItem(dict, "type", msg.type);
DictSafeSetItem(dict, "min", msg.min);
DictSafeSetItem(dict, "max", msg.max);
DictSafeSetItem(dict, "classification", msg.classification);
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// EU River Information System (RIS)
// Water level
AIS_STATUS
ais8_200_24_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_24 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "country", msg.country);
PyObject *id_list = PyList_New(4);
for (size_t i = 0; i < 4; i++)
PyList_SetItem(id_list, i, PyLong_FromLong(msg.gauge_ids[i]));
DictSafeSetItem(dict, "gauge_ids", id_list);
Py_DECREF(id_list);
PyObject *level_list = PyList_New(4);
for (size_t i = 0; i < 4; i++)
PyList_SetItem(level_list, i, PyFloat_FromDouble(msg.levels[i]));
DictSafeSetItem(dict, "levels", level_list);
Py_DECREF(level_list);
return AIS_OK;
}
// River Information System
// Signal status
AIS_STATUS
ais8_200_40_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_40 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "form", msg.form);
DictSafeSetItem(dict, "dir", msg.dir);
DictSafeSetItem(dict, "stream_dir", msg.stream_dir);
DictSafeSetItem(dict, "status_raw", msg.status_raw);
// TODO(schwehr): extract status components
DictSafeSetItem(dict, "spare2", msg.spare2);
return AIS_OK;
}
// River Information System
// Number of persons on board
AIS_STATUS
ais8_200_55_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_200_55 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "crew", msg.crew);
DictSafeSetItem(dict, "passengers", msg.passengers);
DictSafeSetItem(dict, "yet_more_personnel", msg.yet_more_personnel);
PyObject *spare2_list = PyList_New(3);
for (size_t i = 0; i < 3; i++)
PyList_SetItem(spare2_list, 0, PyLong_FromLong(msg.spare2[i]));
DictSafeSetItem(dict, "spare2", spare2_list);
Py_DECREF(spare2_list);
return AIS_OK;
}
void
ais8_367_22_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
Ais8_367_22 msg(nmea_payload, pad); // TODO(schwehr): check for errors
DictSafeSetItem(dict, "version", msg.version);
DictSafeSetItem(dict, "link_id", msg.link_id);
DictSafeSetItem(dict, "notice_type", msg.notice_type);
// TODO(schwehr): are 8:1:22 and 8:367:22 tables the same?
DictSafeSetItem(dict, "notice_type_str",
ais8_1_22_notice_names[msg.notice_type]);
DictSafeSetItem(dict, "month", msg.month); // This is UTC, not local time.
DictSafeSetItem(dict, "day", msg.day);
DictSafeSetItem(dict, "hour", msg.hour);
DictSafeSetItem(dict, "minute", msg.minute);
DictSafeSetItem(dict, "durations_minutes", msg.duration_minutes);
PyObject *sub_area_list = PyList_New(msg.sub_areas.size());
// Loop over sub_areas
for (size_t i = 0; i < msg.sub_areas.size(); i++) {
switch (msg.sub_areas[i]->getType()) {
case AIS8_366_22_SHAPE_CIRCLE: // or point
{
PyObject *sub_area = PyDict_New();
Ais8_367_22_Circle *c =
dynamic_cast<Ais8_367_22_Circle*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_366_22_SHAPE_CIRCLE);
if (c->radius_m == 0)
DictSafeSetItem(sub_area, "sub_area_type_str", "point");
else
DictSafeSetItem(sub_area, "sub_area_type_str", "circle");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "radius", c->radius_m);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_366_22_SHAPE_RECT:
{
PyObject *sub_area = PyDict_New();
Ais8_367_22_Rect *c =
dynamic_cast<Ais8_367_22_Rect*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_366_22_SHAPE_RECT);
DictSafeSetItem(sub_area, "sub_area_type_str", "rect");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "e_dim_m", c->e_dim_m);
DictSafeSetItem(sub_area, "n_dim_m", c->n_dim_m);
DictSafeSetItem(sub_area, "orient_deg", c->orient_deg);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_366_22_SHAPE_SECTOR:
{
PyObject *sub_area = PyDict_New();
Ais8_367_22_Sector *c =
dynamic_cast<Ais8_367_22_Sector*>(msg.sub_areas[i].get());
assert(c != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_366_22_SHAPE_SECTOR);
DictSafeSetItem(sub_area, "sub_area_type_str", "sector");
DictSafeSetItem(sub_area, "x", "y", c->position);
DictSafeSetItem(sub_area, "precision", c->precision);
DictSafeSetItem(sub_area, "radius", c->radius_m);
DictSafeSetItem(sub_area, "left_bound_deg", c->left_bound_deg);
DictSafeSetItem(sub_area, "right_bound_deg", c->right_bound_deg);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_366_22_SHAPE_POLYLINE: // FALLTHROUGH
case AIS8_366_22_SHAPE_POLYGON:
{
PyObject *sub_area = PyDict_New();
Ais8_367_22_Poly *poly =
dynamic_cast<Ais8_367_22_Poly*>(msg.sub_areas[i].get());
assert(poly != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", msg.sub_areas[i]->getType());
if (msg.sub_areas[i]->getType() == AIS8_366_22_SHAPE_POLYLINE)
DictSafeSetItem(sub_area, "sub_area_type_str", "polyline");
else
DictSafeSetItem(sub_area, "sub_area_type_str", "polygon");
assert(poly->angles.size() == poly->dists_m.size());
PyObject *angle_list = PyList_New(poly->angles.size());
PyObject *dist_list = PyList_New(poly->angles.size());
for (size_t pt_num = 0; pt_num < poly->angles.size(); pt_num++) {
PyList_SetItem(angle_list, pt_num,
PyFloat_FromDouble(poly->angles[pt_num]));
PyList_SetItem(dist_list, pt_num,
PyFloat_FromDouble(poly->dists_m[pt_num]));
}
DictSafeSetItem(sub_area, "angles", angle_list);
DictSafeSetItem(sub_area, "dists_m", dist_list);
Py_DECREF(angle_list);
Py_DECREF(dist_list);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
case AIS8_366_22_SHAPE_TEXT:
{
PyObject *sub_area = PyDict_New();
Ais8_367_22_Text *text =
dynamic_cast<Ais8_367_22_Text*>(msg.sub_areas[i].get());
assert(text != nullptr);
DictSafeSetItem(sub_area, "sub_area_type", AIS8_366_22_SHAPE_TEXT);
DictSafeSetItem(sub_area, "sub_area_type_str", "text");
DictSafeSetItem(sub_area, "text", text->text);
PyList_SetItem(sub_area_list, i, sub_area);
}
break;
default:
{} // TODO(schwehr): Mark an unknown subarea or raise an exception.
}
}
DictSafeSetItem(dict, "sub_areas", sub_area_list);
Py_DECREF(sub_area_list);
}
AIS_STATUS
ais8_367_23_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_367_23 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "version", msg.version);
if (msg.utc_day != 0) {
DictSafeSetItem(dict, "utc_day", msg.utc_day);
} else {
DictSafeSetItem(dict, "utc_day", Py_None);
}
if (msg.utc_hour <= 23) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
} else {
DictSafeSetItem(dict, "utc_hour", Py_None);
}
if (msg.utc_min <= 59) {
DictSafeSetItem(dict, "utc_min", msg.utc_min);
} else {
DictSafeSetItem(dict, "utc_min", Py_None);
}
DictSafeSetItem(dict, "x", "y", msg.position);
if (msg.pressure <= (402 + 799)) {
DictSafeSetItem(dict, "pressure", msg.pressure);
} else {
// Raw value was 403 (N/A), or reserved value.
DictSafeSetItem(dict, "pressure", Py_None);
}
if (msg.air_temp_raw >= -600 && msg.air_temp_raw <= 600) {
DictSafeSetItem(dict, "air_temp", msg.air_temp);
} else {
DictSafeSetItem(dict, "air_temp", Py_None);
}
if (msg.wind_speed <= 121) {
DictSafeSetItem(dict, "wind_speed", msg.wind_speed);
} else {
// Raw value was 122 (N/A) or reserved value.
DictSafeSetItem(dict, "wind_speed", Py_None);
}
if (msg.wind_gust <= 121) {
DictSafeSetItem(dict, "wind_gust", msg.wind_gust);
} else {
// Raw value was 122 (N/A) or reserved value.
DictSafeSetItem(dict, "wind_gust", Py_None);
}
if (msg.wind_dir <= 359) {
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
} else {
// Raw value was 360 (N/A), or reserved value.
DictSafeSetItem(dict, "wind_dir", Py_None);
}
return AIS_OK;
}
AIS_STATUS
ais8_367_24_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_367_24 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "version", msg.version);
if (msg.utc_hour <= 23) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
} else {
DictSafeSetItem(dict, "utc_hour", Py_None);
}
if (msg.utc_min <= 59) {
DictSafeSetItem(dict, "utc_min", msg.utc_min);
} else {
DictSafeSetItem(dict, "utc_min", Py_None);
}
DictSafeSetItem(dict, "x", "y", msg.position);
if (msg.pressure <= (403 + 799)) {
DictSafeSetItem(dict, "pressure", msg.pressure);
} else {
// Raw value was 403 (N/A), or reserved value.
DictSafeSetItem(dict, "pressure", Py_None);
}
return AIS_OK;
}
AIS_STATUS
ais8_367_25_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_367_25 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
DictSafeSetItem(dict, "version", msg.version);
if (msg.utc_hour <= 23) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
} else {
DictSafeSetItem(dict, "utc_hour", Py_None);
}
if (msg.utc_min <= 59) {
DictSafeSetItem(dict, "utc_min", msg.utc_min);
} else {
DictSafeSetItem(dict, "utc_min", Py_None);
}
if (msg.pressure <= (799 + 402)) {
DictSafeSetItem(dict, "pressure", msg.pressure);
} else {
// Raw value was 403 (N/A), or reserved value.
DictSafeSetItem(dict, "pressure", Py_None);
}
if (msg.wind_speed <= 121) {
DictSafeSetItem(dict, "wind_speed", msg.wind_speed);
} else {
// Raw value was 122 (N/A) or reserved value.
DictSafeSetItem(dict, "wind_speed", Py_None);
}
if (msg.wind_dir <= 359) {
DictSafeSetItem(dict, "wind_dir", msg.wind_dir);
} else {
// Raw value was 360 (N/A), or reserved value.
DictSafeSetItem(dict, "wind_dir", Py_None);
}
return AIS_OK;
}
AIS_STATUS
ais8_367_33_append_pydict_sensor_hdr(PyObject *dict,
Ais8_367_33_SensorReport* rpt) {
assert(dict);
assert(rpt);
DictSafeSetItem(dict, "report_type", rpt->report_type);
if (rpt->utc_day != 0) {
DictSafeSetItem(dict, "utc_day", rpt->utc_day);
} else {
DictSafeSetItem(dict, "utc_day", Py_None);
}
if (rpt->utc_hr <= 23) {
DictSafeSetItem(dict, "utc_hr", rpt->utc_hr);
} else {
DictSafeSetItem(dict, "utc_hr", Py_None);
}
if (rpt->utc_min <= 59) {
DictSafeSetItem(dict, "utc_min", rpt->utc_min);
} else {
DictSafeSetItem(dict, "utc_min", Py_None);
}
DictSafeSetItem(dict, "site_id", rpt->site_id);
return AIS_OK;
}
AIS_STATUS
ais8_367_33_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais8_367_33 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}
PyObject *rpt_list = PyList_New(msg.reports.size());
DictSafeSetItem(dict, "reports", rpt_list);
for (size_t rpt_num = 0; rpt_num < msg.reports.size(); rpt_num++) {
PyObject *rpt_dict = PyDict_New();
PyList_SetItem(rpt_list, rpt_num, rpt_dict);
switch (msg.reports[rpt_num]->report_type) {
// case AIS8_367_33_SENSOR_ERROR:
case AIS8_367_33_SENSOR_LOCATION:
{
Ais8_367_33_Location *rpt =
dynamic_cast<Ais8_367_33_Location *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "x", "y", rpt->position);
if (rpt->altitude_raw >= -2000 && rpt->altitude_raw <= 2001) {
DictSafeSetItem(rpt_dict, "altitude", rpt->altitude);
} else {
DictSafeSetItem(rpt_dict, "altitude", Py_None);
}
DictSafeSetItem(rpt_dict, "owner", rpt->owner);
DictSafeSetItem(rpt_dict, "timeout", rpt->timeout);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_STATION:
{
Ais8_367_33_Station *rpt =
dynamic_cast<Ais8_367_33_Station *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "name", rpt->name);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_WIND:
{
Ais8_367_33_Wind *rpt =
dynamic_cast<Ais8_367_33_Wind *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->wind_speed >= 0 && rpt->wind_speed <= 121) {
DictSafeSetItem(rpt_dict, "wind_speed", rpt->wind_speed);
} else {
DictSafeSetItem(rpt_dict, "wind_speed", Py_None);
}
if (rpt->wind_gust >= 0 && rpt->wind_gust <= 121) {
DictSafeSetItem(rpt_dict, "wind_gust", rpt->wind_gust);
} else {
DictSafeSetItem(rpt_dict, "wind_gust", Py_None);
}
if (rpt->wind_dir >= 0 && rpt->wind_dir <= 359) {
DictSafeSetItem(rpt_dict, "wind_dir", rpt->wind_dir);
} else {
DictSafeSetItem(rpt_dict, "wind_dir", Py_None);
}
if (rpt->wind_gust_dir >= 0 && rpt->wind_gust_dir <= 359) {
DictSafeSetItem(rpt_dict, "wind_gust_dir", rpt->wind_gust_dir);
} else {
DictSafeSetItem(rpt_dict, "wind_gust_dir", Py_None);
}
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
if (rpt->wind_forecast >= 0 && rpt->wind_forecast <= 121) {
DictSafeSetItem(rpt_dict, "wind_forecast", rpt->wind_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_forecast", Py_None);
}
if (rpt->wind_gust_forecast >= 0 && rpt->wind_gust_forecast <= 121) {
DictSafeSetItem(rpt_dict, "wind_gust_forecast", rpt->wind_gust_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_gust_forecast", Py_None);
}
if (rpt->wind_dir_forecast >= 0 && rpt->wind_dir_forecast <= 359) {
DictSafeSetItem(rpt_dict, "wind_dir_forecast", rpt->wind_dir_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_dir_forecast", Py_None);
}
if (rpt->utc_day_forecast != 0) {
DictSafeSetItem(rpt_dict, "utc_day_forecast", rpt->utc_day_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_day_forecast", Py_None);
}
if (rpt->utc_hour_forecast >= 0 && rpt->utc_hour_forecast <= 23) {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", Py_None);
}
if (rpt->utc_min_forecast >= 0 && rpt->utc_min_forecast <= 59) {
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_min_forecast", Py_None);
}
DictSafeSetItem(rpt_dict, "duration", rpt->duration);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_WATER_LEVEL:
{
Ais8_367_33_WaterLevel *rpt =
dynamic_cast<Ais8_367_33_WaterLevel *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "type", rpt->type);
if (rpt->level != 32768) {
DictSafeSetItem(rpt_dict, "level", rpt->level);
} else {
DictSafeSetItem(rpt_dict, "level", Py_None);
}
DictSafeSetItem(rpt_dict, "trend", rpt->trend);
DictSafeSetItem(rpt_dict, "vdatum", rpt->vdatum);
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
DictSafeSetItem(rpt_dict, "forecast_type", rpt->forecast_type);
if (rpt->level_forecast != 32768) {
DictSafeSetItem(rpt_dict, "level_forecast", rpt->level_forecast);
} else {
DictSafeSetItem(rpt_dict, "level_forecast", Py_None);
}
DictSafeSetItem(rpt_dict, "utc_day_forecast", rpt->utc_day_forecast);
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
DictSafeSetItem(rpt_dict, "duration", rpt->duration);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_CURR_2D:
{
Ais8_367_33_Curr2D *rpt =
dynamic_cast<Ais8_367_33_Curr2D *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "type", rpt->type);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
PyObject *curr_list = PyList_New(3);
DictSafeSetItem(rpt_dict, "currents", curr_list);
for (size_t idx = 0; idx < 3; idx++) {
PyObject *curr_dict = PyDict_New();
if (rpt->currents[idx].speed_raw <= 246) {
DictSafeSetItem(curr_dict, "speed", rpt->currents[idx].speed);
} else {
DictSafeSetItem(curr_dict, "speed", Py_None);
}
if (rpt->currents[idx].dir <= 359) {
DictSafeSetItem(curr_dict, "dir", rpt->currents[idx].dir);
} else {
DictSafeSetItem(curr_dict, "dir", Py_None);
}
if (rpt->currents[idx].depth <= 361) {
DictSafeSetItem(curr_dict, "depth", rpt->currents[idx].depth);
} else {
DictSafeSetItem(curr_dict, "depth", Py_None);
}
PyList_SetItem(curr_list, idx, curr_dict);
}
}
break;
case AIS8_367_33_SENSOR_CURR_3D:
{
Ais8_367_33_Curr3D *rpt =
dynamic_cast<Ais8_367_33_Curr3D *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "type", rpt->type);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
PyObject *curr_list = PyList_New(2);
DictSafeSetItem(rpt_dict, "currents", curr_list);
for (size_t idx = 0; idx < 2; idx++) {
PyObject *curr_dict = PyDict_New();
PyList_SetItem(curr_list, idx, curr_dict);
if (rpt->currents[idx].north_raw >= -251 && rpt->currents[idx].north_raw <= 251) {
DictSafeSetItem(curr_dict, "north", rpt->currents[idx].north);
} else {
DictSafeSetItem(curr_dict, "north", Py_None);
}
if (rpt->currents[idx].east_raw >= -251 && rpt->currents[idx].east_raw <= 251) {
DictSafeSetItem(curr_dict, "east", rpt->currents[idx].east);
} else {
DictSafeSetItem(curr_dict, "east", Py_None);
}
if (rpt->currents[idx].up_raw >= -251 && rpt->currents[idx].up_raw <= 251) {
DictSafeSetItem(curr_dict, "up", rpt->currents[idx].up);
} else {
DictSafeSetItem(curr_dict, "up", Py_None);
}
if (rpt->currents[idx].depth >= 0 && rpt->currents[idx].depth <= 361) {
DictSafeSetItem(curr_dict, "depth", rpt->currents[idx].depth);
} else {
DictSafeSetItem(curr_dict, "depth", Py_None);
}
}
}
break;
case AIS8_367_33_SENSOR_HORZ_FLOW:
{
Ais8_367_33_HorzFlow *rpt =
dynamic_cast<Ais8_367_33_HorzFlow *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
if (rpt->bearing <= 359) {
DictSafeSetItem(rpt_dict, "bearing", rpt->bearing);
} else {
DictSafeSetItem(rpt_dict, "bearing", Py_None);
}
DictSafeSetItem(rpt_dict, "type", rpt->type);
PyObject *curr_list = PyList_New(2);
DictSafeSetItem(rpt_dict, "currents", curr_list);
for (size_t idx = 0; idx < 2; idx++) {
PyObject *curr_dict = PyDict_New();
if (rpt->currents[idx].dist <= 361) {
DictSafeSetItem(curr_dict, "dist", rpt->currents[idx].dist);
} else {
DictSafeSetItem(curr_dict, "dist", Py_None);
}
if (rpt->currents[idx].speed_raw <= 246) {
DictSafeSetItem(curr_dict, "speed", rpt->currents[idx].speed);
} else {
DictSafeSetItem(curr_dict, "speed", Py_None);
}
if (rpt->currents[idx].dir <= 359) {
DictSafeSetItem(curr_dict, "dir", rpt->currents[idx].dir);
} else {
DictSafeSetItem(curr_dict, "dir", Py_None);
}
if (rpt->currents[idx].level <= 361) {
DictSafeSetItem(curr_dict, "level", rpt->currents[idx].level);
} else {
DictSafeSetItem(curr_dict, "level", Py_None);
}
PyList_SetItem(curr_list, idx, curr_dict);
}
}
break;
case AIS8_367_33_SENSOR_SEA_STATE:
{
Ais8_367_33_SeaState *rpt =
dynamic_cast<Ais8_367_33_SeaState *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->swell_height_raw <= 246) {
DictSafeSetItem(rpt_dict, "swell_height", rpt->swell_height);
} else {
DictSafeSetItem(rpt_dict, "swell_height", Py_None);
}
if (rpt->swell_period <= 60) {
DictSafeSetItem(rpt_dict, "swell_period", rpt->swell_period);
} else {
DictSafeSetItem(rpt_dict, "swell_period", Py_None);
}
if (rpt->swell_dir <= 359) {
DictSafeSetItem(rpt_dict, "swell_dir", rpt->swell_dir);
} else {
DictSafeSetItem(rpt_dict, "swell_dir", Py_None);
}
DictSafeSetItem(rpt_dict, "sea_state", rpt->sea_state);
DictSafeSetItem(rpt_dict, "swell_sensor_type", rpt->swell_sensor_type);
if (rpt->water_temp_raw <= 600) {
DictSafeSetItem(rpt_dict, "water_temp", rpt->water_temp);
} else {
DictSafeSetItem(rpt_dict, "water_temp", Py_None);
}
if (rpt->water_temp_depth_raw <= 121) {
DictSafeSetItem(rpt_dict, "water_temp_depth", rpt->water_temp_depth);
} else {
DictSafeSetItem(rpt_dict, "water_temp_depth", Py_None);
}
DictSafeSetItem(rpt_dict, "water_sensor_type", rpt->water_sensor_type);
if (rpt->wave_height_raw <= 246) {
DictSafeSetItem(rpt_dict, "wave_height", rpt->wave_height);
} else {
DictSafeSetItem(rpt_dict, "wave_height", Py_None);
}
if (rpt->wave_period <= 60) {
DictSafeSetItem(rpt_dict, "wave_period", rpt->wave_period);
} else {
DictSafeSetItem(rpt_dict, "wave_period", Py_None);
}
if (rpt->wave_dir <= 359) {
DictSafeSetItem(rpt_dict, "wave_dir", rpt->wave_dir);
} else {
DictSafeSetItem(rpt_dict, "wave_dir", Py_None);
}
DictSafeSetItem(rpt_dict, "wave_sensor_type", rpt->wave_sensor_type);
if (rpt->salinity_raw <= 501) {
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
} else {
DictSafeSetItem(rpt_dict, "salinity", Py_None);
}
}
break;
case AIS8_367_33_SENSOR_SALINITY:
{
Ais8_367_33_Salinity *rpt =
dynamic_cast<Ais8_367_33_Salinity *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->water_temp_raw <= 600) {
DictSafeSetItem(rpt_dict, "water_temp", rpt->water_temp);
} else {
DictSafeSetItem(rpt_dict, "water_temp", Py_None);
}
if (rpt->conductivity_raw <= 701) {
DictSafeSetItem(rpt_dict, "conductivity", rpt->conductivity);
} else {
DictSafeSetItem(rpt_dict, "conductivity", Py_None);
}
if (rpt->pressure_raw <= 60001) {
DictSafeSetItem(rpt_dict, "pressure", rpt->pressure);
} else {
DictSafeSetItem(rpt_dict, "pressure", Py_None);
}
if (rpt->salinity_raw <= 501) {
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
} else {
DictSafeSetItem(rpt_dict, "salinity", Py_None);
}
DictSafeSetItem(rpt_dict, "salinity_type", rpt->salinity_type);
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
DictSafeSetItem(rpt_dict, "spare0", rpt->spare2[0]);
DictSafeSetItem(rpt_dict, "spare1", rpt->spare2[1]);
}
break;
case AIS8_367_33_SENSOR_WX:
{
Ais8_367_33_Wx *rpt =
dynamic_cast<Ais8_367_33_Wx *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->air_temp_raw >= -600 && rpt->air_temp_raw <= 600) {
DictSafeSetItem(rpt_dict, "air_temp", rpt->air_temp);
} else {
DictSafeSetItem(rpt_dict, "air_temp", Py_None);
}
DictSafeSetItem(rpt_dict, "air_temp_sensor_type",
rpt->air_temp_sensor_type);
DictSafeSetItem(rpt_dict, "precip", rpt->precip);
if (rpt->horz_vis_raw <= 241) {
DictSafeSetItem(rpt_dict, "horz_vis", rpt->horz_vis);
} else {
DictSafeSetItem(rpt_dict, "horz_vis", Py_None);
}
// Not sure how to map 702 and 703 to Python.
if (rpt->dew_point_raw <= 700) {
DictSafeSetItem(rpt_dict, "dew_point", rpt->dew_point);
} else {
DictSafeSetItem(rpt_dict, "dew_point", Py_None);
}
DictSafeSetItem(rpt_dict, "dew_point_type", rpt->dew_point_type);
if (rpt->air_pressure_raw <= 402) {
DictSafeSetItem(rpt_dict, "air_pressure", rpt->air_pressure);
} else {
DictSafeSetItem(rpt_dict, "air_pressure", Py_None);
}
DictSafeSetItem(rpt_dict, "air_pressure_trend",
rpt->air_pressure_trend);
DictSafeSetItem(rpt_dict, "air_pressure_sensor_type", rpt->air_pressure_sensor_type);
if (rpt->salinity_raw <= 501) {
DictSafeSetItem(rpt_dict, "salinity", rpt->salinity);
} else {
DictSafeSetItem(rpt_dict, "salinity", Py_None);
}
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_AIR_GAP:
{
Ais8_367_33_AirGap *rpt =
dynamic_cast<Ais8_367_33_AirGap *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->air_draught != 0) {
DictSafeSetItem(rpt_dict, "air_draught", rpt->air_draught);
} else {
DictSafeSetItem(rpt_dict, "air_draught", Py_None);
}
if (rpt->air_gap != 0) {
DictSafeSetItem(rpt_dict, "air_gap", rpt->air_gap);
} else {
DictSafeSetItem(rpt_dict, "air_gap", Py_None);
}
DictSafeSetItem(rpt_dict, "air_gap_trend", rpt->air_gap_trend);
if (rpt->predicted_air_gap != 0) {
DictSafeSetItem(rpt_dict, "predicted_air_gap", rpt->predicted_air_gap);
} else {
DictSafeSetItem(rpt_dict, "predicted_air_gap", Py_None);
}
if (rpt->utc_day_forecast != 0) {
DictSafeSetItem(rpt_dict, "utc_day_forecast", rpt->utc_day_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_day_forecast", Py_None);
}
if (rpt->utc_hour_forecast <= 23) {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", Py_None);
}
if (rpt->utc_min_forecast <= 59) {
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_min_forecast", Py_None);
}
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_WIND_REPORT_2:
{
Ais8_367_33_Wind_V2 *rpt =
dynamic_cast<Ais8_367_33_Wind_V2 *>(msg.reports[rpt_num].get());
assert(rpt != nullptr);
ais8_367_33_append_pydict_sensor_hdr(rpt_dict, rpt);
if (rpt->wind_speed <= 121) {
DictSafeSetItem(rpt_dict, "wind_speed", rpt->wind_speed);
} else {
DictSafeSetItem(rpt_dict, "wind_speed", Py_None);
}
if (rpt->wind_gust <= 121) {
DictSafeSetItem(rpt_dict, "wind_gust", rpt->wind_gust);
} else {
DictSafeSetItem(rpt_dict, "wind_gust", Py_None);
}
if (rpt->wind_dir <= 359) {
DictSafeSetItem(rpt_dict, "wind_dir", rpt->wind_dir);
} else {
DictSafeSetItem(rpt_dict, "wind_dir", Py_None);
}
if (rpt->averaging_time >= 1 && rpt->averaging_time <= 61) {
DictSafeSetItem(rpt_dict, "averaging_time", rpt->averaging_time);
} else {
DictSafeSetItem(rpt_dict, "averaging_time", Py_None);
}
DictSafeSetItem(rpt_dict, "sensor_type", rpt->sensor_type);
if (rpt->wind_speed_forecast <= 121) {
DictSafeSetItem(rpt_dict, "wind_speed_forecast", rpt->wind_speed_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_speed_forecast", Py_None);
}
if (rpt->wind_gust_forecast <= 121) {
DictSafeSetItem(rpt_dict, "wind_gust_forecast", rpt->wind_gust_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_gust_forecast", Py_None);
}
if (rpt->wind_dir_forecast <= 359) {
DictSafeSetItem(rpt_dict, "wind_dir_forecast", rpt->wind_dir_forecast);
} else {
DictSafeSetItem(rpt_dict, "wind_dir_forecast", Py_None);
}
if (rpt->utc_hour_forecast <= 23) {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", rpt->utc_hour_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_hour_forecast", Py_None);
}
if (rpt->utc_min_forecast <= 59) {
DictSafeSetItem(rpt_dict, "utc_min_forecast", rpt->utc_min_forecast);
} else {
DictSafeSetItem(rpt_dict, "utc_min_forecast", Py_None);
}
DictSafeSetItem(rpt_dict, "duration", rpt->duration);
DictSafeSetItem(rpt_dict, "spare2", rpt->spare2);
}
break;
case AIS8_367_33_SENSOR_RESERVED_12:
case AIS8_367_33_SENSOR_RESERVED_13:
case AIS8_367_33_SENSOR_RESERVED_14:
case AIS8_367_33_SENSOR_RESERVED_15:
default:
{} // TODO(schwehr): mark a bad sensor type or raise exception
}
}
return AIS_OK;
}
// AIS Binary broadcast messages. There will be a huge number of subtypes
// If we don't know how to decode it, just return the dac, fi
PyObject*
ais8_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais8 msg(nmea_payload, pad);
if (msg.had_error() && msg.get_error() != AIS_UNINITIALIZED) {
PyErr_Format(ais_py_exception, "Ais8: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "dac", msg.dac);
DictSafeSetItem(dict, "fi", msg.fi);
AIS_STATUS status = AIS_UNINITIALIZED;
switch (msg.dac) {
case AIS_DAC_1_INTERNATIONAL: // IMO.
switch (msg.fi) {
// See: ITU-R.M.1371-3 IFM messages Annex 5, Section 5 and IMO Circ 289
case AIS_FI_8_1_0_TEXT:
status = ais8_1_0_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_11_MET_HYDRO: // Not to be used after 1 Jan 2013.
status = ais8_1_11_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_13_FAIRWAY_CLOSED: // Not to be used after 1 Jan 2013.
// TODO(schwehr): untested - no messages found
status = ais8_1_13_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_15_SHIP_AND_VOYAGE: // Not after 1 Jan 2013.
// TODO(schwehr): untested - no messages found
status = ais8_1_15_append_pydict(nmea_payload, dict, pad);
break;
// 16 has conflicting definition in the old 1371-1: VTS targets.
case AIS_FI_8_1_16_PERSONS_ON_BOARD: // Not to be used after 1 Jan 2013.
status = ais8_1_16_append_pydict(nmea_payload, dict, pad);
break;
// 17 has conflicting definitions in 1371-1: IFM 17: Ship waypoints
// and/or route plan report.
case AIS_FI_8_1_17_VTS_TARGET: // Not to be used after Jan 2013.
// TODO(schwehr): Untested. no messages found.
status = ais8_1_17_append_pydict(nmea_payload, dict, pad);
break;
// ITU 1371-1 conflict: IFM 19: Extended ship static and voyage.
case AIS_FI_8_1_19_TRAFFIC_SIGNAL:
// TODO(schwehr): Untested. No messages found.
status = ais8_1_19_append_pydict(nmea_payload, dict, pad);
DictSafeSetItem(dict, "parsed", true);
break;
// 20: Berthing data.
case AIS_FI_8_1_21_WEATHER_OBS:
// TODO(schwehr): untested - no messages found
status = ais8_1_21_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_22_AREA_NOTICE:
status = ais8_1_22_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_24_SHIP_AND_VOYAGE:
status = ais8_1_24_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_26_SENSOR:
status = ais8_1_26_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_27_ROUTE:
status = ais8_1_27_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_29_TEXT:
status = ais8_1_29_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_31_MET_HYDRO:
status = ais8_1_31_append_pydict(nmea_payload, dict, pad);
break;
// ITU 1371-1 only: 3.10 - IFM 40: Number of persons on board.
default:
DictSafeSetItem(dict, "parsed", false);
break;
}
break;
case AIS_DAC_200_RIS:
switch (msg.fi) {
case AIS_FI_8_200_10_RIS_SHIP_AND_VOYAGE:
status = ais8_200_10_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_200_21_RIS_ETA_AT_LOCK_BRIDGE_TERMINAL:
status = ais8_200_21_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_200_22_RIS_RTA_AT_LOCK_BRIDGE_TERMINAL:
status = ais8_200_22_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_200_23_RIS_EMMA_WARNING:
status = ais8_200_23_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_1_24_SHIP_AND_VOYAGE:
status = ais8_200_24_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_200_40_RIS_ATON_SIGNAL_STATUS:
status = ais8_200_40_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_200_55_RIS_PERSONS_ON_BOARD:
status = ais8_200_55_append_pydict(nmea_payload, dict, pad);
break;
default:
DictSafeSetItem(dict, "parsed", false);
break;
}
break;
// TODO(schwehr): AIS_FI_8_366_22_AREA_NOTICE.
case 367: // United states.
switch (msg.fi) {
case 22: // USCG Area Notice 2012 (v5?).
ais8_367_22_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_367_23_SSW:
status = ais8_367_23_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_367_24_SSW_SMALL:
status = ais8_367_24_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_367_25_SSW_TINY:
status = ais8_367_25_append_pydict(nmea_payload, dict, pad);
break;
case AIS_FI_8_367_33_ENVIRONMENTAL:
status = ais8_367_33_append_pydict(nmea_payload, dict, pad);
break;
default:
DictSafeSetItem(dict, "parsed", false);
break;
}
break;
default:
DictSafeSetItem(dict, "parsed", false);
// TODO(schwehr): raise exception or return standin?
}
if (status != AIS_OK) {
Py_DECREF(dict);
PyErr_Format(ais_py_exception, "Ais8: %s",
AIS_STATUS_STRINGS[status]);
return nullptr;
}
return dict;
}
// Aircraft position report
PyObject*
ais9_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais9 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais9: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "alt", msg.alt);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "timestamp", msg.timestamp);
DictSafeSetItem(dict, "alt_sensor", msg.alt_sensor);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "dte", msg.dte);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "assigned_mode", msg.assigned_mode);
DictSafeSetItem(dict, "raim", msg.raim);
DictSafeSetItem(dict, "sync_state", msg.sync_state);
if (0 == msg.commstate_flag) {
// SOTMDA
DictSafeSetItem(dict, "slot_timeout", msg.slot_timeout);
if (msg.received_stations_valid)
DictSafeSetItem(dict, "received_stations", msg.received_stations);
if (msg.slot_number_valid)
DictSafeSetItem(dict, "slot_number", msg.slot_number);
if (msg.utc_valid) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "utc_spare", msg.utc_spare);
}
if (msg.slot_offset_valid)
DictSafeSetItem(dict, "slot_offset", msg.slot_offset);
} else {
// ITDMA
DictSafeSetItem(dict, "slot_increment", msg.slot_increment);
DictSafeSetItem(dict, "slots_to_allocate", msg.slots_to_allocate);
DictSafeSetItem(dict, "keep_flag", msg.keep_flag);
}
return dict;
}
// 10 - ':' - UTC and date inquiry
PyObject*
ais10_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
assert(pad < 6);
Ais10 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais10: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "dest_mmsi", msg.dest_mmsi);
DictSafeSetItem(dict, "spare2", msg.spare2);
return dict;
}
// msg 11 ';' - See msg 4_11
// 12 - '<' - Addressed safety related text
PyObject*
ais12_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais12 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais12: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "seq_num", msg.seq_num);
DictSafeSetItem(dict, "dest_mmsi", msg.dest_mmsi);
DictSafeSetItem(dict, "retransmitted", msg.retransmitted);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "text", msg.text);
return dict;
}
// msg 13 - See msg 7
// 14 - '>' - Safety broadcast text
PyObject*
ais14_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais14 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais14: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "text", msg.text);
DictSafeSetItem(dict, "spare2", msg.spare2);
return dict;
}
// 15 - '?' - Interrogation
PyObject*
ais15_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais15 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais15: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "mmsi_1", msg.mmsi_1);
DictSafeSetItem(dict, "msg_1_1", msg.msg_1_1);
DictSafeSetItem(dict, "slot_offset_1_1", msg.slot_offset_1_1);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "dest_msg_1_2", msg.dest_msg_1_2);
DictSafeSetItem(dict, "slot_offset_1_2", msg.slot_offset_1_2);
DictSafeSetItem(dict, "spare3", msg.spare3);
DictSafeSetItem(dict, "mmsi_2", msg.mmsi_2);
DictSafeSetItem(dict, "msg_2", msg.msg_2);
DictSafeSetItem(dict, "slot_offset_2", msg.slot_offset_2);
DictSafeSetItem(dict, "spare4", msg.spare4);
return dict;
}
// 16 - '@' - Assigned mode command
PyObject*
ais16_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais16 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais16: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "dest_mmsi_a", msg.dest_mmsi_a);
DictSafeSetItem(dict, "offset_a", msg.offset_a);
DictSafeSetItem(dict, "inc_a", msg.inc_a);
if (-1 != msg.spare2) DictSafeSetItem(dict, "spare2", msg.spare2);
if (-1 != msg.dest_mmsi_b) {
DictSafeSetItem(dict, "dest_mmsi_b", msg.dest_mmsi_b);
DictSafeSetItem(dict, "offset_b", msg.offset_b);
DictSafeSetItem(dict, "inc_b", msg.inc_b);
}
return dict;
}
// 17 - 'A' - GNSS differential - TODO(schwehr): incomplete
PyObject*
ais17_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais17 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais17: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "spare2", msg.spare2);
return dict;
}
// 18 - 'B' - Class B position report.
PyObject*
ais18_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais18 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais18: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "true_heading", msg.true_heading);
DictSafeSetItem(dict, "timestamp", msg.timestamp);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "unit_flag", msg.unit_flag);
DictSafeSetItem(dict, "display_flag", msg.display_flag);
DictSafeSetItem(dict, "dsc_flag", msg.dsc_flag);
DictSafeSetItem(dict, "band_flag", msg.band_flag);
DictSafeSetItem(dict, "m22_flag", msg.m22_flag);
DictSafeSetItem(dict, "mode_flag", msg.mode_flag);
DictSafeSetItem(dict, "raim", msg.raim);
DictSafeSetItem(dict, "commstate_flag", msg.commstate_flag);
if (msg.slot_timeout_valid) {
DictSafeSetItem(dict, "slot_timeout", msg.slot_timeout);
}
if (msg.slot_offset_valid) {
DictSafeSetItem(dict, "slot_offset", msg.slot_offset);
}
if (msg.utc_valid) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "utc_spare", msg.utc_spare);
}
if (msg.slot_number_valid) {
DictSafeSetItem(dict, "slot_number", msg.slot_number);
}
if (msg.received_stations_valid) {
DictSafeSetItem(dict, "received_stations", msg.received_stations);
}
// ITDMA
if (msg.slot_increment_valid) {
DictSafeSetItem(dict, "slot_increment", msg.slot_increment);
DictSafeSetItem(dict, "slots_to_allocate", msg.slots_to_allocate);
DictSafeSetItem(dict, "keep_flag", msg.keep_flag);
}
if (msg.commstate_cs_fill_valid) {
DictSafeSetItem(dict, "commstate_cs_fill", msg.commstate_cs_fill);
}
return dict;
}
// 19 - 'C' - Class B combined position report and ship data
PyObject*
ais19_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais19 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais19: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "true_heading", msg.true_heading);
DictSafeSetItem(dict, "timestamp", msg.timestamp);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "name", msg.name);
DictSafeSetItem(dict, "type_and_cargo", msg.type_and_cargo);
DictSafeSetItem(dict, "dim_a", msg.dim_a);
DictSafeSetItem(dict, "dim_b", msg.dim_b);
DictSafeSetItem(dict, "dim_c", msg.dim_c);
DictSafeSetItem(dict, "dim_d", msg.dim_d);
DictSafeSetItem(dict, "fix_type", msg.fix_type);
DictSafeSetItem(dict, "raim", msg.raim);
DictSafeSetItem(dict, "dte", msg.dte);
DictSafeSetItem(dict, "assigned_mode", msg.assigned_mode);
DictSafeSetItem(dict, "spare3", msg.spare3);
return dict;
}
// 20 - 'D' - data link management
PyObject*
ais20_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais20 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais20: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
int list_size = 1;
if (msg.group_valid_4) list_size = 4;
else if (msg.group_valid_3) list_size = 3;
else if (msg.group_valid_2) list_size = 2;
PyObject *list = PyList_New(list_size);
{
PyObject *reservation = PyDict_New();
DictSafeSetItem(reservation, "offset", msg.offset_1);
DictSafeSetItem(reservation, "num_slots", msg.num_slots_1);
DictSafeSetItem(reservation, "timeout", msg.timeout_1);
DictSafeSetItem(reservation, "incr", msg.incr_1);
PyList_SetItem(list, 0, reservation);
}
if (msg.group_valid_2) {
PyObject *reservation = PyDict_New();
DictSafeSetItem(reservation, "offset", msg.offset_2);
DictSafeSetItem(reservation, "num_slots", msg.num_slots_2);
DictSafeSetItem(reservation, "timeout", msg.timeout_2);
DictSafeSetItem(reservation, "incr", msg.incr_2);
PyList_SetItem(list, 1, reservation);
}
if (msg.group_valid_3) {
PyObject *reservation = PyDict_New();
DictSafeSetItem(reservation, "offset", msg.offset_3);
DictSafeSetItem(reservation, "num_slots", msg.num_slots_3);
DictSafeSetItem(reservation, "timeout", msg.timeout_3);
DictSafeSetItem(reservation, "incr", msg.incr_3);
PyList_SetItem(list, 2, reservation);
}
if (msg.group_valid_4) {
PyObject *reservation = PyDict_New();
DictSafeSetItem(reservation, "offset", msg.offset_4);
DictSafeSetItem(reservation, "num_slots", msg.num_slots_4);
DictSafeSetItem(reservation, "timeout", msg.timeout_4);
DictSafeSetItem(reservation, "incr", msg.incr_4);
PyList_SetItem(list, 3, reservation);
}
PyDict_SetItemString(dict, "reservations", list);
Py_DECREF(list);
return dict;
}
// 21 - 'E' - ATON Aid to Navigation
PyObject*
ais21_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais21 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais21: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "aton_type", msg.aton_type);
DictSafeSetItem(dict, "name", msg.name);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "dim_a", msg.dim_a);
DictSafeSetItem(dict, "dim_b", msg.dim_b);
DictSafeSetItem(dict, "dim_c", msg.dim_c);
DictSafeSetItem(dict, "dim_d", msg.dim_d);
DictSafeSetItem(dict, "fix_type", msg.fix_type);
DictSafeSetItem(dict, "timestamp", msg.timestamp);
DictSafeSetItem(dict, "off_pos", msg.off_pos);
DictSafeSetItem(dict, "aton_status", msg.aton_status);
DictSafeSetItem(dict, "raim", msg.raim);
DictSafeSetItem(dict, "virtual_aton", msg.virtual_aton);
DictSafeSetItem(dict, "assigned_mode", msg.assigned_mode);
return dict;
}
// 22 - 'F' - Channel management
PyObject*
ais22_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais22 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais22: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "chan_a", msg.chan_a);
DictSafeSetItem(dict, "chan_b", msg.chan_b);
DictSafeSetItem(dict, "txrx_mode", msg.txrx_mode);
DictSafeSetItem(dict, "power_low", msg.power_low);
if (msg.pos_valid) {
DictSafeSetItem(dict, "x1", "y1", msg.position1);
DictSafeSetItem(dict, "x2", "y2", msg.position2);
} else {
DictSafeSetItem(dict, "dest_mmsi_1", msg.dest_mmsi_1);
DictSafeSetItem(dict, "dest_mmsi_2", msg.dest_mmsi_2);
}
DictSafeSetItem(dict, "chan_a_bandwidth", msg.chan_a_bandwidth);
DictSafeSetItem(dict, "chan_b_bandwidth", msg.chan_b_bandwidth);
DictSafeSetItem(dict, "zone_size", msg.zone_size);
DictSafeSetItem(dict, "spare2", msg.spare2);
return dict;
}
// 23 - 'F' - Group assignment command
PyObject*
ais23_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais23 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais23: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "spare", msg.spare);
DictSafeSetItem(dict, "x1", "y1", msg.position1);
DictSafeSetItem(dict, "x2", "y2", msg.position2);
DictSafeSetItem(dict, "station_type", msg.station_type);
DictSafeSetItem(dict, "type_and_cargo", msg.type_and_cargo);
DictSafeSetItem(dict, "spare2", msg.spare2);
DictSafeSetItem(dict, "txrx_mode", msg.txrx_mode);
DictSafeSetItem(dict, "interval_raw", msg.interval_raw);
DictSafeSetItem(dict, "quiet", msg.quiet);
DictSafeSetItem(dict, "spare3", msg.spare3);
return dict;
}
// 24 - 'H' - Static data report
PyObject*
ais24_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais24 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais24: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "part_num", msg.part_num);
switch (msg.part_num) {
case 0: // Part A
DictSafeSetItem(dict, "name", msg.name);
break;
case 1: // Part B
DictSafeSetItem(dict, "type_and_cargo", msg.type_and_cargo);
DictSafeSetItem(dict, "vendor_id", msg.vendor_id);
DictSafeSetItem(dict, "callsign", msg.callsign);
DictSafeSetItem(dict, "dim_a", msg.dim_a);
DictSafeSetItem(dict, "dim_b", msg.dim_b);
DictSafeSetItem(dict, "dim_c", msg.dim_c);
DictSafeSetItem(dict, "dim_d", msg.dim_d);
DictSafeSetItem(dict, "spare", msg.spare);
break;
case 2: // FALLTHROUGH - not yet defined by ITU
case 3: // FALLTHROUGH - not yet defined by ITU
default:
// status = AIS_ERR_BAD_MSG_CONTENT;
Py_DECREF(dict);
PyErr_Format(ais_py_exception, "Ais24: unknown part_num %d",
msg.part_num);
return nullptr;
}
return dict;
}
// 25 - 'I' - Single slot binary message
PyObject*
ais25_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Ais25 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais25: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
// TODO(schwehr) use_app_id
if (msg.dest_mmsi_valid) DictSafeSetItem(dict, "dest_mmsi", msg.dest_mmsi);
if (msg.use_app_id) {
DictSafeSetItem(dict, "dac", msg.dac);
DictSafeSetItem(dict, "fi", msg.fi);
}
// TODO(schwehr): handle payload
return dict;
}
// 26 - 'J' - Multi-slot binary message with commstate
PyObject*
ais26_to_pydict(const char *nmea_payload, const size_t pad) {
Ais26 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais26: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
if (msg.dest_mmsi_valid) DictSafeSetItem(dict, "dest_mmsi", msg.dest_mmsi);
if (msg.use_app_id) {
DictSafeSetItem(dict, "dac", msg.dac);
DictSafeSetItem(dict, "fi", msg.fi);
}
// TODO(schwehr): handle payload
DictSafeSetItem(dict, "sync_state", msg.sync_state);
if (0 == msg.commstate_flag) {
// SOTDMA
if (msg.received_stations_valid)
DictSafeSetItem(dict, "received_stations", msg.received_stations);
if (msg.slot_number_valid)
DictSafeSetItem(dict, "slot_number", msg.slot_number);
if (msg.utc_valid) {
DictSafeSetItem(dict, "utc_hour", msg.utc_hour);
DictSafeSetItem(dict, "utc_min", msg.utc_min);
DictSafeSetItem(dict, "utc_spare", msg.utc_spare);
}
if (msg.slot_offset_valid)
DictSafeSetItem(dict, "slot_offset", msg.slot_offset);
} else {
// ITDMA
DictSafeSetItem(dict, "slot_increment", msg.slot_increment);
DictSafeSetItem(dict, "slots_to_allocate", msg.slots_to_allocate);
DictSafeSetItem(dict, "keep_flag", msg.keep_flag);
}
return dict;
}
// 27 - 'K' - Short position report for satellite reception
PyObject*
ais27_to_pydict(const char *nmea_payload, const size_t pad) {
Ais27 msg(nmea_payload, pad);
if (msg.had_error()) {
PyErr_Format(ais_py_exception, "Ais27: %s",
AIS_STATUS_STRINGS[msg.get_error()]);
return nullptr;
}
PyObject *dict = ais_msg_to_pydict(&msg);
DictSafeSetItem(dict, "position_accuracy", msg.position_accuracy);
DictSafeSetItem(dict, "raim", msg.raim);
DictSafeSetItem(dict, "nav_status", msg.nav_status);
DictSafeSetItem(dict, "x", "y", msg.position);
DictSafeSetItem(dict, "sog", msg.sog);
DictSafeSetItem(dict, "cog", msg.cog);
DictSafeSetItem(dict, "gnss", msg.gnss);
DictSafeSetItem(dict, "spare", msg.spare);
return dict;
}
extern "C" {
static PyObject *
decode(PyObject *self, PyObject *args) {
int _pad;
const char *nmea_payload;
// TODO(schwehr): what to do about if no pad bits? Maybe warn and set to 0?
if (!PyArg_ParseTuple(args, "si", &nmea_payload, &_pad)) {
_pad = 0;
if (!PyArg_ParseTuple(args, "s", &nmea_payload)) {
PyErr_Format(ais_py_exception, "ais.decode: expected (str, int)");
return nullptr;
}
}
const size_t pad = _pad;
// The grand dispatcher
switch (nmea_payload[0]) {
case '1': // FALLTHROUGH - Class A Position
case '2': // FALLTHROUGH
case '3':
return ais1_2_3_to_pydict(nmea_payload, pad);
case '4': // FALLTHROUGH - 4 - Basestation report
case ';': // 11 - UTC date response
return ais4_11_to_pydict(nmea_payload, pad);
case '5': // 5 - Ship and Cargo
return ais5_to_pydict(nmea_payload, pad);
case '6': // 6 - Addressed binary message
return ais6_to_pydict(nmea_payload, pad);
case '7': // FALLTHROUGH - 7 - ACK for addressed binary message
case '=': // 13 - ASRM Ack (safety message)
return ais7_13_to_pydict(nmea_payload, pad);
case '8': // 8 - Binary broadcast message (BBM)
return ais8_to_pydict(nmea_payload, pad);
case '9': // 9 - SAR Position
return ais9_to_pydict(nmea_payload, pad);
case ':': // 10 - UTC Query
return ais10_to_pydict(nmea_payload, pad);
// ';' 11 - See 4
case '<': // 12 - ASRM
return ais12_to_pydict(nmea_payload, pad);
// 13 - See 7
case '>': // 14 - SRBM - Safety broadcast
return ais14_to_pydict(nmea_payload, pad);
case '?': // 15 - Interrogation
return ais15_to_pydict(nmea_payload, pad);
case '@': // 16 - Assigned mode command
return ais16_to_pydict(nmea_payload, pad);
case 'A': // 17 - GNSS broadcast
return ais17_to_pydict(nmea_payload, pad);
case 'B': // 18 - Position, Class B
return ais18_to_pydict(nmea_payload, pad);
case 'C': // 19 - Position and ship, Class B
return ais19_to_pydict(nmea_payload, pad);
case 'D': // 20 - Data link management
return ais20_to_pydict(nmea_payload, pad);
case 'E': // 21 - Aids to navigation report
return ais21_to_pydict(nmea_payload, pad);
case 'F': // 22 - Channel Management
return ais22_to_pydict(nmea_payload, pad);
case 'G': // 23 - Group Assignment Command
return ais23_to_pydict(nmea_payload, pad);
case 'H': // 24 - Static data report
return ais24_to_pydict(nmea_payload, pad);
case 'I': // 25 - Single slot binary message - addressed or broadcast
// TODO(schwehr): handle payloads
return ais25_to_pydict(nmea_payload, pad);
case 'J': // 26 - Multi slot binary message with comm state
return ais26_to_pydict(nmea_payload, pad); // TODO(schwehr): payloads
case 'K': // 27 - Long-range AIS broadcast message
return ais27_to_pydict(nmea_payload, pad);
case 'L': // 28 - UNKNOWN
PyErr_Format(ais_py_exception, "ais.decode: message 28 (L) not handled");
break;
default:
PyErr_Format(ais_py_exception, "ais.decode: unknown message - %c",
nmea_payload[0]);
}
return nullptr;
}
static PyMethodDef ais_methods[] = {
{"decode", decode, METH_VARARGS, "Return a dictionary for a NMEA string"},
{nullptr, nullptr, 0, nullptr}, // Sentinel
};
// Python module initialization
static struct PyModuleDef aismodule = {
PyModuleDef_HEAD_INIT,
"_ais", // Module name.
nullptr, // Module documentation.
-1, // Says the module keeps state in global variables.
ais_methods
};
PyMODINIT_FUNC PyInit__ais(void) {
PyObject *module = PyModule_Create(&aismodule);
if (module == nullptr)
return nullptr;
ais_py_exception = PyErr_NewException(exception_name, nullptr, nullptr);
Py_INCREF(ais_py_exception);
PyModule_AddObject(module, exception_short, ais_py_exception);
return module;
}
} // extern "C"
} // namespace libais