Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/ais6 235 10 #195

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/libais/ais.h
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,23 @@ class Ais6_1_40 : public Ais6 {
};
ostream& operator<< (ostream &o, const Ais6_1_40 &msg);

// Number of persons on board. ITU 1371-1
class Ais6_235_10 : public Ais6 {
public:
float ana_int;
float ana_ext1;
float ana_ext2;
int racon;
int light;
bool health;
int stat_ext;
bool off_pos;
int spare2;

Ais6_235_10(const char *nmea_payload, const size_t pad);
};
ostream& operator<< (ostream &o, const Ais6_235_10 &msg);

//////////////////////////////////////////////////////////////////////

// 7 and 13 are ACKs for msg 6 and 12
Expand Down
35 changes: 34 additions & 1 deletion src/libais/ais6.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Ais6::Ais6(const char *nmea_payload, const size_t pad)
bits.SeekTo(38);
seq = bits.ToUnsignedInt(38, 2);
mmsi_dest = bits.ToUnsignedInt(40, 30);
retransmit = !bits[70];
retransmit = static_cast<bool>(bits[70]);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ! not is missing. Also, why is a static_cast<bool> better?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From ais type 6 documentation, it should not be negated !:

70 1 Retransmit flag retransmit b 0 = no retransmit (default) 1 = retransmitted

The decoding of the messages type 6 from the tests:

    {
        'nmea': ['!AIVDM,1,1,,A,63m95T8uBK:0044@00P,2*7A'],
        'result': {
            'id': 6,
            'repeat_indicator': 0,
            'mmsi': 257050000,
            'seq': 2,
            'mmsi_dest': 257060000,
            'retransmit': True,
            'spare': 0,
            'dac': 1,
            'fi': 1,
            'ack_dac': 64,
            'msg_seq': 1,
            'spare2': 0}},
    {
        'nmea': ['!AIVDM,1,1,,B,65@<;:1inW@h0480J0,4*60'],
        'result': {
            'id': 6,
            'repeat_indicator': 0,
            'mmsi': 352521000,
            'seq': 0,
            'mmsi_dest': 477535500,
            'retransmit': True,
            'spare': 0,
            'dac': 1,
            'fi': 2,
            'req_dac': 1,
            'req_fi': 40}}

are not correct, as confirmed by decoding the AIS messages for instance here.

Therefore changed tests that are affected + this line.

@schwehr reverted to not using static_cast<bool>.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use original source documentation for AIS message 6. I certainly could have been wrong, but unless you quote an official standard like ITU M.1371, I am not going to change it. gpsd aivdm docs were written by ESR with feedback from me before ITU M.1371 was freely available. I had a purchased copy; ESR did not.

So, I believe that I set this up as a bool called retransmit based on discussions at RTCM SC121 back in the 2007-2009 time frame.

3.4 Message 6: Addressed binary message; table 54:

Retransmit flag 1 Retransmit flag should be set upon retransmission: 0 = no retransmission =
default; 1 = retransmitted

I took the flag as a should retransmit.

No matter the correct answer, changing it in this pr is out of scope. Please don't do it here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@schwehr you are right, topic is out of scope for this PR. Will use original source doc in another issue. 👍

Reverted.

spare = bits[71];
dac = bits.ToUnsignedInt(72, 10);
fi = bits.ToUnsignedInt(82, 6);
Expand Down Expand Up @@ -561,4 +561,37 @@ Ais6_1_40::Ais6_1_40(const char *nmea_payload, const size_t pad)
status = AIS_OK;
}

// This message provides AtoN (Aid to navigation) monitoring data for the General Lighthouse Authorities (GLA)
Ais6_235_10::Ais6_235_10(const char *nmea_payload, const size_t pad)
: Ais6(nmea_payload, pad), ana_int(0.0), ana_ext1(0.0), ana_ext2(0.0), racon(0), light(0), health(0), stat_ext(0), off_pos(0), spare2(0) {
assert(dac == 235);
assert(fi == 10);

if (num_bits != 136) {
status = AIS_ERR_BAD_BIT_COUNT;
return;
}

AisBitset bs;
const AIS_STATUS r = bits.ParseNmeaPayload(nmea_payload, pad);
if (r != AIS_OK) {
status = r;
return;
}

bits.SeekTo(88);
ana_int = bits.ToUnsignedInt(88, 10) / 20.;
ana_ext1 = bits.ToUnsignedInt(98, 10) / 20.;
ana_ext2 = bits.ToUnsignedInt(108, 10) / 20.;
racon = bits.ToUnsignedInt(118, 2);
light = bits.ToUnsignedInt(120, 2);
health = static_cast<bool>(bits[122]);
stat_ext = bits.ToUnsignedInt(123, 8);
off_pos = static_cast<bool>(bits[131]);
spare2 = bits.ToUnsignedInt(132, 4);

assert(bits.GetRemaining() == 0);
status = AIS_OK;
}

} // namespace libais
129 changes: 81 additions & 48 deletions src/libais/ais_py.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ enum AIS_FI {
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_6_235_10_ATON_MONITORING_DATA = 10,
AIS_FI_8_1_0_TEXT = 0,
AIS_FI_8_1_11_MET_HYDRO = 11,
AIS_FI_8_1_13_FAIRWAY_CLOSED = 13,
Expand Down Expand Up @@ -698,6 +698,30 @@ ais6_1_40_append_pydict(const char *nmea_payload, PyObject *dict,
return AIS_OK;
}

AIS_STATUS
ais6_235_10_append_pydict(const char *nmea_payload, PyObject *dict,
const size_t pad) {
assert(nmea_payload);
assert(dict);
assert(pad < 6);
Ais6_235_10 msg(nmea_payload, pad);
if (msg.had_error()) {
return msg.get_error();
}

DictSafeSetItem(dict, "ana_int", msg.ana_int);
DictSafeSetItem(dict, "ana_ext1", msg.ana_ext1);
DictSafeSetItem(dict, "ana_ext2", msg.ana_ext2);
DictSafeSetItem(dict, "racon", msg.racon);
DictSafeSetItem(dict, "light", msg.light);
DictSafeSetItem(dict, "health", msg.health);
DictSafeSetItem(dict, "stat_ext", msg.stat_ext);
DictSafeSetItem(dict, "off_pos", msg.off_pos);
DictSafeSetItem(dict, "spare2", msg.spare2);

return AIS_OK;
}

PyObject*
ais6_to_pydict(const char *nmea_payload, const size_t pad) {
assert(nmea_payload);
Expand All @@ -722,58 +746,67 @@ ais6_to_pydict(const char *nmea_payload, const size_t pad) {
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);
case AIS_DAC_1_INTERNATIONAL: // IMO.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't reindent this all.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok. back to original indenting.

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;
case AIS_FI_6_1_40_PERSONS_ON_BOARD: // OLD ITU 1371-1.
status = ais6_1_40_append_pydict(nmea_payload, dict, pad);
case AIS_DAC_235_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND: // IMO.
switch (msg.fi) {
case AIS_FI_6_235_10_ATON_MONITORING_DATA: // IALA-A126.
status = ais6_235_10_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);
}
break;

default:
// TODO(schwehr): Raise an exception?
DictSafeSetItem(dict, "not_parsed", true);
}

if (status != AIS_OK) {
Expand Down
7 changes: 7 additions & 0 deletions src/libais/decode_body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ unique_ptr<AisMsg> CreateAisMsg6(const string &body, const int fill_bits) {
}
// FI not handled.
break;
case libais::AIS_DAC_235_UNITED_KINGDOM_OF_GREAT_BRITAIN_AND_NORTHERN_IRELAND:
switch (msg.fi) {
case 10:
return MakeUnique<libais::Ais6_235_10>(body.c_str(), fill_bits);
}
// FI not handled.
break;
}
return nullptr;
}
Expand Down
31 changes: 26 additions & 5 deletions test/test_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@
'mmsi_dest': 538090218,
'msg_seq': 0,
'repeat_indicator': 0,
'retransmit': False,
'retransmit': True,
'seq': 2,
'spare': 0,
'spare2': 0,
Expand All @@ -153,7 +153,7 @@
'mmsi': 257050000,
'seq': 2,
'mmsi_dest': 257060000,
'retransmit': True,
'retransmit': False,
'spare': 0,
'dac': 1,
'fi': 1,
Expand All @@ -168,7 +168,7 @@
'mmsi': 352521000,
'seq': 0,
'mmsi_dest': 477535500,
'retransmit': True,
'retransmit': False,
'spare': 0,
'dac': 1,
'fi': 2,
Expand All @@ -184,7 +184,7 @@
'mmsi_dest': 205523890,
'repeat_indicator': 0,
'req_dac': 1,
'retransmit': False,
'retransmit': True,
'seq': 2,
'spare': 0,
'spare2': 0,
Expand All @@ -211,7 +211,7 @@
'mmsi': 205323000,
'mmsi_dest': 2053501,
'repeat_indicator': 0,
'retransmit': True,
'retransmit': False,
'seq': 0,
'spare': 0,
'spare2': 0,
Expand Down Expand Up @@ -673,4 +673,25 @@
'y': -2.5533333333333332}
},

{
'nmea': [ '!AIVDM,1,1,4,B,6>jR0600V:C0>da4P106P00,2*02' ],
'result': {'id': 6,
'repeat_indicator': 0,
'mmsi': 992509976,
'seq': 0,
'mmsi_dest': 2500912,
'retransmit': False,
'spare': 0,
'dac': 235,
'fi': 10,
'ana_int': 13.699999809265137,
'ana_ext1': 0.05000000074505806,
'ana_ext2': 0.05000000074505806,
'racon': 2,
'light': 2,
'health': False,
'stat_ext': 0,
'off_pos': False,
'spare2': 0}
},
]
2 changes: 2 additions & 0 deletions test/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ def DictDiff(a, b):
def Compare(x, y):
if x == y:
return True
if x in [None, 'nan'] and y in [None, 'nan']:
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need this? And that says that None and 'nan' are ==. Is that really what we want?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modified because testTypeExamples fails. Not ideal. Reverted.

return True
x = TextToNumber(x)
y = TextToNumber(y)
if isinstance(x, six.string_types) and isinstance(y, six.string_types):
Expand Down