Repository: M0r13n/pyais

Issue:
Default value for NavigationStatus enum
In `messages.py` the status field in message types 1, 2, 3 and 27 uses the NavigationStatus enum as a converter - however the default value defined in the field is set to 0.

A naive reading of this would dictate that a value of 0 would be set for this enum where one isn't set or readable from the AIS message. 

As 0 is actually a meaningful value in this enum, would it not make more sense to set a default of 15, which is "undefined" in the enum, and the default according to the AIS spec at https://gpsd.gitlab.io/gpsd/AIVDM.html

Discussion:
@M0r13n: Hey @l0x,

yep, you are right. I've change the default value to `undefined` for relevant enums. I won't create a release for this fix, so you can expect the changes to be available in the next release.

Have a great day,
Leon

Solution patch:
diff --git a/pyais/messages.py b/pyais/messages.py
index 026ad76..e1db359 100644
--- a/pyais/messages.py
+++ b/pyais/messages.py
@@ -919,7 +919,7 @@ class MessageType1(Payload, CommunicationStateMixin):
     msg_type = bit_field(6, int, default=1, signed=False)
     repeat = bit_field(2, int, default=0, signed=False)
     mmsi = bit_field(30, int, from_converter=from_mmsi)
-    status = bit_field(4, int, default=0, converter=NavigationStatus.from_value, signed=False)
+    status = bit_field(4, int, default=NavigationStatus.Undefined, converter=NavigationStatus.from_value, signed=False)
     turn = bit_field(8, float, default=TurnRate.NO_TI_DEFAULT, signed=True, to_converter=to_turn, from_converter=from_turn)
     speed = bit_field(10, float, from_converter=from_speed, to_converter=to_speed, default=0, signed=False)
     accuracy = bit_field(1, bool, default=0, signed=False)
@@ -928,7 +928,7 @@ class MessageType1(Payload, CommunicationStateMixin):
     course = bit_field(12, float, from_converter=from_10th, to_converter=to_10th, default=0, signed=False)
     heading = bit_field(9, int, default=0, signed=False)
     second = bit_field(6, int, default=0, signed=False)
-    maneuver = bit_field(2, int, default=0, from_converter=ManeuverIndicator.from_value,
+    maneuver = bit_field(2, int, default=ManeuverIndicator.UNDEFINED, from_converter=ManeuverIndicator.from_value,
                          to_converter=ManeuverIndicator.from_value, signed=False)
     spare_1 = bit_field(3, bytes, default=b'')
     raim = bit_field(1, bool, default=0)
@@ -969,7 +969,7 @@ class MessageType4(Payload, CommunicationStateMixin):
     accuracy = bit_field(1, bool, default=0, signed=False)
     lon = bit_field(28, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
     lat = bit_field(27, float, from_converter=from_lat_lon, to_converter=to_lat_lon, signed=True, default=0)
-    epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value,
+    epfd = bit_field(4, int, default=EpfdType.Undefined, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value,
                      signed=False)
     spare_1 = bit_field(10, bytes, default=b'')
     raim = bit_field(1, bool, default=0)
@@ -994,7 +994,7 @@ class MessageType5(Payload):
     to_stern = bit_field(9, int, default=0, signed=False)
     to_port = bit_field(6, int, default=0, signed=False)
     to_starboard = bit_field(6, int, default=0, signed=False)
-    epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
+    epfd = bit_field(4, int, default=EpfdType.Undefined, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
     month = bit_field(4, int, default=0, signed=False)
     day = bit_field(5, int, default=0, signed=False)
     hour = bit_field(5, int, default=0, signed=False)
@@ -1258,7 +1258,7 @@ class MessageType19(Payload):
     to_stern = bit_field(9, int, default=0, signed=False)
     to_port = bit_field(6, int, default=0, signed=False)
     to_starboard = bit_field(6, int, default=0, signed=False)
-    epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
+    epfd = bit_field(4, int, default=EpfdType.Undefined, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
     raim = bit_field(1, bool, default=0)
     dte = bit_field(1, bool, default=0)
     assigned = bit_field(1, bool, default=0, signed=False)
@@ -1319,7 +1319,7 @@ class MessageType21(Payload):
     to_port = bit_field(6, int, default=0, signed=False)
     to_starboard = bit_field(6, int, default=0, signed=False)
 
-    epfd = bit_field(4, int, default=0, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
+    epfd = bit_field(4, int, default=EpfdType.Undefined, from_converter=EpfdType.from_value, to_converter=EpfdType.from_value)
     second = bit_field(6, int, default=0, signed=False)
     off_position = bit_field(1, bool, default=0)
     reserved_1 = bit_field(8, int, default=0, signed=False)
@@ -1731,7 +1731,7 @@ class MessageType27(Payload):
 
     accuracy = bit_field(1, bool, default=0, signed=False)
     raim = bit_field(1, bool, default=0, signed=False)
-    status = bit_field(4, int, default=0, from_converter=NavigationStatus, to_converter=NavigationStatus, signed=False)
+    status = bit_field(4, int, default=NavigationStatus.Undefined, from_converter=NavigationStatus, to_converter=NavigationStatus, signed=False)
     lon = bit_field(18, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
     lat = bit_field(17, float, from_converter=from_lat_lon_600, to_converter=to_lat_lon_600, default=0, signed=True)
     speed = bit_field(6, float, default=0, signed=False)


Test patch:
diff --git a/tests/test_decode.py b/tests/test_decode.py
index d74a152..83c956c 100644
--- a/tests/test_decode.py
+++ b/tests/test_decode.py
@@ -1350,22 +1350,22 @@ def test_msg_type_6_json_reverse(self):
 
     def test_rot_encode_yields_expected_values(self):
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": 25.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nh600000000000000000000,0*05"
+        assert encoded == "!AIVDO,1,1,,A,10000Nw60000000000000001P000,0*7B"
 
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": -16.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nhs@0000000000000000000,0*30"
+        assert encoded == "!AIVDO,1,1,,A,10000Nws@000000000000001P000,0*4E"
 
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": 4.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nh2@0000000000000000000,0*71"
+        assert encoded == "!AIVDO,1,1,,A,10000Nw2@000000000000001P000,0*0F"
 
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": -4.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nhuh0000000000000000000,0*1E"
+        assert encoded == "!AIVDO,1,1,,A,10000Nwuh000000000000001P000,0*60"
 
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": -121.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nhk00000000000000000000,0*58"
+        assert encoded == "!AIVDO,1,1,,A,10000Nwk0000000000000001P000,0*26"
 
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": 64.0})[0]
-        assert encoded == "!AIVDO,1,1,,A,10000Nh9P0000000000000000000,0*6A"
+        assert encoded == "!AIVDO,1,1,,A,10000Nw9P000000000000001P000,0*14"
 
     def test_rot_encode_decode(self):
         encoded = encode_dict({"msg_type": 1, "mmsi": 123, "turn": 2.0})[0]
diff --git a/tests/test_encode.py b/tests/test_encode.py
index 700acec..bf11843 100644
--- a/tests/test_encode.py
+++ b/tests/test_encode.py
@@ -3,6 +3,7 @@
 import bitarray
 
 from pyais import encode_dict, encode_msg
+from pyais.constants import NavigationStatus
 from pyais.decode import decode
 from pyais.encode import data_to_payload, get_ais_type
 from pyais.exceptions import UnknownPartNoException
@@ -1020,7 +1021,25 @@ def test_encode_msg_type2():
         'type': 2
     }
     encoded = encode_dict(data)[0]
-    assert encoded == "!AIVDO,1,1,,A,1S9edj0003PecbBN`ja@0?w42000,0*4A"
+    assert encoded == "!AIVDO,1,1,,A,1S9edj?003PecbBN`ja@0?w5R000,0*24"
+
+    data = {
+        'accuracy': 1,
+        'course': 0.0,
+        'heading': 511,
+        'lat': 53.542675,
+        'lon': 9.97942833333333,
+        'mmsi': 211512520,
+        'raim': 1,
+        'repeat': 2,
+        'second': 34,
+        'speed': 0.3,
+        'turn': 0,
+        'type': 2,
+        'status': NavigationStatus.UnderWayUsingEngine,
+    }
+    encoded = encode_dict(data)[0]
+    assert encoded == "!AIVDO,1,1,,A,1S9edj0003PecbBN`ja@0?w5R000,0*2B"
 
 
 def test_encode_msg_type_3():
@@ -1042,7 +1061,7 @@ def test_encode_msg_type_3():
     }
 
     encoded = encode_dict(data)[0]
-    assert encoded == "!AIVDO,1,1,,A,15NSH95001G?wopE`beasVk@0E5:,0*6F"
+    assert encoded == "!AIVDO,1,1,,A,15NSH95001G?wopE`beasVkAPE5:,0*0E"
 
 
 def test_encode_type_1_default():
@@ -1051,7 +1070,7 @@ def test_encode_type_1_default():
     """
     data = {'mmsi': 123456789, 'type': 1}
     encoded = encode_dict(data)[0]
-    assert encoded == "!AIVDO,1,1,,A,11mg=5@P00000000000000000000,0*36"
+    assert encoded == "!AIVDO,1,1,,A,11mg=5OP0000000000000001P000,0*58"
 
 
 def test_encode_type_1():
@@ -1088,7 +1107,7 @@ def test_mmsi_too_long():
     encoded = encode_msg(msg)
     decoded = decode(encoded[0])
 
-    assert encoded[0] == "!AIVDO,1,1,,A,1?wwwwhP00000000000000000000,0*12"
+    assert encoded[0] == "!AIVDO,1,1,,A,1?wwwwwP0000000000000001P000,0*6C"
     assert decoded.mmsi == 1073741823
 
 
@@ -1097,7 +1116,7 @@ def test_lon_too_large():
     encoded = encode_msg(msg)
     decoded = decode(encoded[0])
 
-    assert encoded[0] == "!AIVDO,1,1,,A,10000NhP00Owwwv0000000000000,0*1D"
+    assert encoded[0] == "!AIVDO,1,1,,A,10000NwP00Owwwv000000001P000,0*63"
     assert decoded.lon == -2e-06