text: import textwrap
  import unittest
  from pprint import pprint
  
  from pyais import NMEAMessage, encode_dict
  from pyais.ais_types import AISType
  from pyais.constants import ManeuverIndicator, NavigationStatus, ShipType, NavAid, EpfdType, StationType, TransmitMode
  from pyais.decode import decode
  from pyais.exceptions import UnknownMessageException
  from pyais.messages import MessageType18, MessageType5, MessageType6, MSG_CLASS
  from pyais.stream import ByteStream
  
  
  class TestAIS(unittest.TestCase):
      """
      TestCases for AIS message decoding and assembling.
  
      The Test messages are from multiple sources and are scrambled together.
      Raw messages are decoded by either hand or some online decoder.
      As my main source of AIS messages I used this dumb:
      https://www.aishub.net/ais-dispatcher
  
      As my main decoder I used this decoder:
      http://ais.tbsalling.dk
  
      The latter sometimes is a bit weird and therefore I used aislib to verify my results.
      """
  
      def test_to_json(self):
          json_dump = decode(b"!AIVDM,1,1,,A,15NPOOPP00o?b=bE`UNv4?w428D;,0*24").to_json()
          text = textwrap.dedent("""{
      "msg_type": 1,
      "repeat": 0,
      "mmsi": "367533950",
      "status": 0,
      "turn": -128,
      "speed": 0.0,
      "accuracy": 1,
      "lon": -122.408232,
      "lat": 37.808418,
      "course": 360.0,
      "heading": 511,
      "second": 34,
      "maneuver": 0,
      "spare": 0,
      "raim": true,
      "radio": 34059
  }""")
          self.assertEqual(json_dump, text)
  
      @unittest.skip("TODO")
      def test_msg_type(self):
          """
          Test if msg type is correct
          """
          nmea = NMEAMessage(b"!AIVDM,1,1,,B,15M67FC000G?ufbE`FepT@3n00Sa,0*5C")
          assert nmea.decode().msg_type == AISType.POS_CLASS_A1
  
          nmea = NMEAMessage(b"!AIVDM,1,1,,B,15NG6V0P01G?cFhE`R2IU?wn28R>,0*05")
          assert nmea.decode().msg_type == AISType.POS_CLASS_A1
  
          nmea = NMEAMessage.assemble_from_iterable(messages=[
              NMEAMessage(b"!AIVDM,2,1,4,A,55O0W7`00001L@gCWGA2uItLth@DqtL5@F22220j1h742t0Ht0000000,0*08"),
              NMEAMessage(b"!AIVDM,2,2,4,A,000000000000000,2*20")
          ])
          assert nmea.decode().msg_type == AISType.STATIC_AND_VOYAGE
  
      def test_msg_type_1_a(self):
          result = decode(b"!AIVDM,1,1,,B,15M67FC000G?ufbE`FepT@3n00Sa,0*5C").asdict()
          assert result == {
              'msg_type': 1,
              'repeat': 0,
              'mmsi': '366053209',
              'status': NavigationStatus.RestrictedManoeuverability,
              'turn': 0,
              'speed': 0.0,
              'accuracy': 0,
              'lon': -122.341618,
              'lat': 37.802118,
              'course': 219.3,
              'heading': 1,
              'second': 59,
              'maneuver': ManeuverIndicator.NotAvailable,
              'raim': False,
              'radio': 2281,
              'spare': 0
          }
  
      def test_msg_type_1_b(self):
          msg = decode(b"!AIVDM,1,1,,A,15NPOOPP00o?b=bE`UNv4?w428D;,0*24").asdict()
          assert msg['msg_type'] == 1
          assert msg['mmsi'] == '367533950'
          assert msg['repeat'] == 0
          assert msg['status'] == NavigationStatus.UnderWayUsingEngine
          assert msg['turn'] == -128
          assert msg['speed'] == 0
          assert msg['accuracy'] == 1
          assert round(msg['lat'], 4) == 37.8084
          assert round(msg['lon'], 4) == -122.4082
          assert msg['course'] == 360
          assert msg['heading'] == 511
          assert msg['second'] == 34
          assert msg['maneuver'] == ManeuverIndicator.NotAvailable
          assert msg['raim']
          assert isinstance(msg['raim'], bool)
  
      def test_msg_type_1_c(self):
          msg = decode(b"!AIVDM,1,1,,B,181:Kjh01ewHFRPDK1s3IRcn06sd,0*08").asdict()
          assert msg['course'] == 87.0
          assert msg['mmsi'] == '538090443'
          assert msg['speed'] == 10.9
  
      def test_decode_pos_1_2_3(self):
          # weired message of type 0 as part of issue #4
          content = decode(b"!AIVDM,1,1,,B,0S9edj0P03PecbBN`ja@0?w42cFC,0*7C").asdict()
  
          assert content['repeat'] == 2
          assert content['mmsi'] == "211512520"
          assert content['turn'] == -128
          assert content['speed'] == 0.3
          assert round(content['lat'], 4) == 53.5427
          assert round(content['lon'], 4) == 9.9794
          assert round(content['course'], 1) == 0.0
  
          assert decode(b"!AIVDM,1,1,,B,0S9edj0P03PecbBN`ja@0?w42cFC,0*7C").to_json()
  
      def test_decode_1_speed(self):
          content = decode(b"!AIVDM,1,1,,A,13@nePh01>PjcO4PGReoJEmL0HJg,0*67").asdict()
  
          assert content['speed'] == 7.8
          assert content['msg_type'] == 1
  
      def test_msg_type_3(self):
          msg = decode(b"!AIVDM,1,1,,A,35NSH95001G?wopE`beasVk@0E5:,0*6F").asdict()
          assert msg['msg_type'] == 3
          assert msg['mmsi'] == "367581220"
          assert msg['repeat'] == 0
          assert msg['status'] == NavigationStatus.Moored
          assert msg['turn'] == 0
          assert msg['speed'] == 0.1
          assert msg['accuracy'] == 0
          assert round(msg['lat'], 4) == 37.8107
          assert round(msg['lon'], 4) == -122.3343
          assert round(msg['course'], 1) == 254.2
          assert msg['heading'] == 217
          assert msg['second'] == 40
          assert msg['maneuver'] == ManeuverIndicator.NotAvailable
          assert not msg['raim']
  
      def test_msg_type_4_a(self):
          msg = decode(b"!AIVDM,1,1,,A,403OviQuMGCqWrRO9>E6fE700@GO,0*4D").asdict()
          assert msg['lon'] == -76.352362
          assert msg['lat'] == 36.883767
          assert msg['accuracy'] == 1
          assert msg['year'] == 2007
          assert msg['month'] == 5
          assert msg['day'] == 14
          assert msg['minute'] == 57
          assert msg['second'] == 39
  
      def test_msg_type_4_b(self):
          msg = decode(b"!AIVDM,1,1,,B,403OtVAv>lba;o?Ia`E`4G?02H6k,0*44").asdict()
          assert round(msg['lon'], 4) == -122.4648
          assert round(msg['lat'], 4) == 37.7943
          assert msg['mmsi'] == "003669145"
          assert msg['accuracy'] == 1
          assert msg['year'] == 2019
          assert msg['month'] == 11
          assert msg['day'] == 9
          assert msg['hour'] == 10
          assert msg['minute'] == 41
          assert msg['second'] == 11
          assert msg['epfd'].value == 0
          assert msg['epfd'] == EpfdType.Undefined
  
      def test_msg_type_5(self):
          msg = decode(
              "!AIVDM,2,1,1,A,55?MbV02;H;s<HtKR20EHE:0@T4@Dn2222222216L961O5Gf0NSQEp6ClRp8,0*1C",
              "!AIVDM,2,2,1,A,88888888880,2*25"
          ).asdict()
          assert msg['callsign'] == "3FOF8"
          assert msg['shipname'] == "EVER DIADEM"
          assert msg['ship_type'] == ShipType.Cargo
          assert msg['to_bow'] == 225
          assert msg['to_stern'] == 70
          assert msg['to_port'] == 1
          assert msg['to_starboard'] == 31
          assert msg['draught'] == 12.2
          assert msg['destination'] == "NEW YORK"
          assert msg['dte'] == 0
          assert msg['epfd'] == EpfdType.GPS
  
      def test_msg_type_6(self):
          msg = decode(b"!AIVDM,1,1,,B,6B?n;be:cbapalgc;i6?Ow4,2*4A").asdict()
          assert msg['seqno'] == 3
          assert msg['dest_mmsi'] == "313240222"
          assert msg['mmsi'] == "150834090"
          assert msg['dac'] == 669
          assert msg['fid'] == 11
          assert not msg['retransmit']
          assert msg['data'] == 258587390607345
  
      def test_msg_type_7(self):
          msg = decode(b"!AIVDM,1,1,,A,702R5`hwCjq8,0*6B").asdict()
          assert msg['mmsi'] == "002655651"
          assert msg['msg_type'] == 7
          assert msg['mmsi1'] == "265538450"
          assert msg['mmsiseq1'] == 0
          assert msg['mmsi2'] is None
          assert msg['mmsi3'] is None
          assert msg['mmsi4'] is None
  
      def test_msg_type_8(self):
          msg = decode(b"!AIVDM,1,1,,A,85Mwp`1Kf3aCnsNvBWLi=wQuNhA5t43N`5nCuI=p<IBfVqnMgPGs,0*47").asdict()
  
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "366999712"
          assert msg['dac'] == 366
          assert msg['fid'] == 56
          assert msg['data'] == 26382309960366212432958701962051450160982977169991995217145869625277036758523
  
      def test_msg_type_9(self):
          msg = decode(b"!AIVDM,1,1,,B,91b55wi;hbOS@OdQAC062Ch2089h,0*30").asdict()
          assert msg['msg_type'] == 9
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "111232511"
          assert msg['alt'] == 303
          assert msg['speed'] == 42
          assert msg['accuracy'] == 0
          assert round(msg['lon'], 5) == -6.27884
          assert round(msg['lat'], 5) == 58.144
          assert msg['course'] == 154.5
          assert msg['second'] == 15
          assert msg['dte'] == 1
          assert msg['radio'] == 33392
          assert not msg['raim']
          assert isinstance(msg['raim'], bool)
  
      def test_msg_type_10_a(self):
          msg = decode(b"!AIVDM,1,1,,B,:5MlU41GMK6@,0*6C").asdict()
          assert msg['dest_mmsi'] == "366832740"
          assert msg['repeat'] == 0
  
      def test_msg_type_10_b(self):
          msg = decode(b"!AIVDM,1,1,,B,:6TMCD1GOS60,0*5B").asdict()
          assert msg['dest_mmsi'] == "366972000"
          assert msg['repeat'] == 0
  
      def test_msg_type_11(self):
          msg = decode(b"!AIVDM,1,1,,B,;4R33:1uUK2F`q?mOt@@GoQ00000,0*5D").asdict()
          assert round(msg['lon'], 4) == -94.4077
          assert round(msg['lat'], 4) == 28.4091
          assert msg['accuracy'] == 1
          assert msg['msg_type'] == 11
          assert msg['year'] == 2009
          assert msg['month'] == 5
          assert msg['day'] == 22
          assert msg['hour'] == 2
          assert msg['minute'] == 22
          assert msg['second'] == 40
  
      def test_msg_type_12_a(self):
          msg = decode(b"!AIVDM,1,1,,A,<5?SIj1;GbD07??4,0*38").asdict()
          assert msg['msg_type'] == 12
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "351853000"
          assert msg['seqno'] == 0
          assert msg['dest_mmsi'] == "316123456"
          assert msg['retransmit'] == 0
          assert msg['text'] == "GOOD"
  
      def test_msg_type_12_b(self):
          msg = decode(b"!AIVDM,1,1,,A,<42Lati0W:Ov=C7P6B?=Pjoihhjhqq0,2*2B")
          assert msg.msg_type == 12
          assert msg.repeat == 0
          assert msg.mmsi == "271002099"
          assert msg.seqno == 0
          assert msg.dest_mmsi == "271002111"
          assert msg.retransmit == 1
  
      def test_msg_type_13(self):
          msg = decode(b"!AIVDM,1,1,,A,=39UOj0jFs9R,0*65").asdict()
          assert msg['msg_type'] == 13
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "211378120"
          assert msg['mmsi1'] == "211217560"
  
      def test_msg_type_14(self):
          msg = decode(b"!AIVDM,1,1,,A,>5?Per18=HB1U:1@E=B0m<L,2*51").asdict()
          assert msg['msg_type'] == 14
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "351809000"
          assert msg['text'] == "RCVD YR TEST MSG"
  
      def test_msg_type_15_a(self):
          msg = decode(b"!AIVDM,1,1,,A,?5OP=l00052HD00,2*5B").asdict()
          assert msg['msg_type'] == 15
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "368578000"
          assert msg['offset1_1'] == 0
          assert msg['mmsi1'] == '000005158'
          assert msg['offset1_2'] is None
          assert msg['mmsi2'] is None
  
      def test_msg_type_15_b(self):
          msg = decode(b"!AIVDM,1,1,,B,?h3Ovn1GP<K0<P@59a0,2*04").asdict()
          assert msg['msg_type'] == 15
          assert msg['repeat'] == 3
          assert msg['mmsi'] == "003669720"
          assert msg['mmsi1'] == "367014320"
          assert msg['mmsi2'] == "000000000"
          assert msg['type1_1'] == 3
          assert msg['type1_2'] == 5
          assert msg['offset1_2'] == 617
          assert msg['offset1_1'] == 516
  
      def test_msg_type_16(self):
          msg = decode(b"!AIVDM,1,1,,A,@01uEO@mMk7P<P00,0*18").asdict()
  
          assert msg['msg_type'] == 16
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "002053501"
          assert msg['mmsi1'] == "224251000"
          assert msg['offset1'] == 200
          assert msg['increment1'] == 0
          assert msg['mmsi2'] == '000000000'
          assert msg['offset2'] is None
          assert msg['increment2'] is None
  
      def test_msg_type_17_a(self):
          msg = decode(
              b"!AIVDM,2,1,5,A,A02VqLPA4I6C07h5Ed1h<OrsuBTTwS?r:C?w`?la<gno1RTRwSP9:BcurA8a,0*3A",
              b"!AIVDM,2,2,5,A,:Oko02TSwu8<:Jbb,0*11"
          ).asdict()
          n = 0x7c0556c07031febbf52924fe33fa2933ffa0fd2932fdb7062922fe3809292afde9122929fcf7002923ffd20c29aaaa
          assert msg['msg_type'] == 17
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "002734450"
          assert msg['lon'] == 1747.8
          assert msg['lat'] == 3599.2
          assert msg['data'] == n
  
      def test_msg_type_17_b(self):
          msg = decode(b"!AIVDM,1,1,,A,A0476BQ>J8`<h2JpH:4P0?j@2mTEw8`=DP1DEnqvj0,0*79").asdict()
          assert msg['msg_type'] == 17
          assert msg['repeat'] == 0
          assert msg['mmsi'] == "004310602"
          assert msg['lat'] == 2058.2
          assert msg['lon'] == 8029.0
          assert msg['data'] == 0x26b860a12000fc900b5915fc8a0d520054576e7ec80
  
      def test_msg_type_18(self):
          msg = decode(b"!AIVDM,1,1,,A,B5NJ;PP005l4ot5Isbl03wsUkP06,0*76").asdict()
          assert msg['msg_type'] == 18
          assert msg['mmsi'] == "367430530"
          assert msg['speed'] == 0.0
          assert msg['accuracy'] == 0
          assert round(msg['lat'], 2) == 37.79
          assert round(msg['lon'], 2) == -122.27
          assert msg['course'] == 0
          assert msg['heading'] == 511
          assert msg['second'] == 55
          assert msg['reserved_2'] == 0
          assert msg['cs'] == 1
          assert msg['display'] == 0
          assert msg['dsc'] == 1
          assert msg['band'] == 1
          assert msg['msg22'] == 1
          assert not msg['assigned']
          assert not msg['raim']
          assert isinstance(msg['raim'], bool)
  
          assert isinstance(msg['lat'], float)
          assert isinstance(msg['lon'], float)
          assert isinstance(msg['speed'], float)
          assert isinstance(msg['course'], float)
  
      def test_msg_type_18_speed(self):
          msg = decode(b"!AIVDO,1,1,,A,B5NJ;PP2aUl4ot5Isbl6GwsUkP06,0*35").asdict()
  
          assert msg['speed'] == 67.8
          assert msg['course'] == 10.1
  
      def test_msg_type_19(self):
          msg = decode(b"!AIVDM,1,1,,B,C5N3SRgPEnJGEBT>NhWAwwo862PaLELTBJ:V00000000S0D:R220,0*0B").asdict()
          assert msg['msg_type'] == 19
          assert msg['mmsi'] == "367059850"
          assert msg['speed'] == 8.7
          assert msg['accuracy'] == 0
          assert msg['lat'] == 29.543695
          assert msg['lon'], 2 == -88.810394
          assert round(msg['course'], 2) == 335.9
          assert msg['heading'] == 511
          assert msg['second'] == 46
          assert msg['shipname'] == "CAPT.J.RIMES"
          assert msg['ship_type'] == ShipType(70)
          assert msg['ship_type'] == ShipType.Cargo
          assert msg['to_bow'] == 5
          assert msg['to_stern'] == 21
          assert msg['to_port'] == 4
          assert msg['to_starboard'] == 4
          assert msg['epfd'] == EpfdType.GPS
          assert msg['dte'] == 0
          assert msg['assigned'] == 0
  
      def test_msg_type_20(self):
          msg = decode(b"!AIVDM,1,1,,A,D028rqP<QNfp000000000000000,2*0C").asdict()
          assert msg['msg_type'] == 20
          assert msg['mmsi'] == "002243302"
          assert msg['offset1'] == 200
          assert msg['number1'] == 5
          assert msg['timeout1'] == 7
          assert msg['increment1'] == 750
  
          # All other values are zero
          for k, v in msg.items():
              if k not in ('msg_type', 'mmsi', 'offset1', 'number1', 'timeout1', 'increment1'):
                  assert not v
  
      def test_msg_type_21(self):
          msg = decode(
              b"!AIVDM,2,1,7,B,E4eHJhPR37q0000000000000000KUOSc=rq4h00000a,0*4A",
              b"!AIVDM,2,2,7,B,@20,4*54"
          ).asdict()
          assert msg['msg_type'] == 21
          assert msg['mmsi'] == "316021442"
          assert msg['aid_type'] == NavAid.REFERENCE_POINT
          assert msg['name'] == "DFO2"
          assert msg['accuracy'] == 1
          assert msg['lat'] == 48.65457
          assert msg['lon'] == -123.429155
          assert not msg['to_bow']
          assert not msg['to_stern']
          assert not msg['to_port']
          assert not msg['to_starboard']
  
          assert msg['off_position']
          assert msg['regional'] == 0
          assert msg['raim']
          assert msg['virtual_aid'] == 0
          assert msg['assigned'] == 0
          assert msg['name_ext'] is None
          assert msg['epfd'] == EpfdType.GPS
  
      def test_msg_type_22_broadcast(self):
          # Broadcast
          msg = decode(b"!AIVDM,1,1,,B,F030p:j2N2P5aJR0r;6f3rj10000,0*11").asdict()
          assert msg['msg_type'] == 22
          assert msg['mmsi'] == "003160107"
          assert msg['channel_a'] == 2087
          assert msg['channel_b'] == 2088
          assert msg['power'] == 0
  
          assert msg['ne_lon'] == -7710.0
          assert msg['ne_lat'] == 3300.0
          assert msg['sw_lon'] == -8020.0
          assert msg['sw_lat'] == 3210.0
  
          assert msg['band_a'] == 0
          assert msg['band_b'] == 0
          assert msg['zonesize'] == 2
  
          assert 'dest1' not in msg.keys()
          assert 'dest2' not in msg.keys()
  
          assert isinstance(msg['ne_lon'], float)
          assert isinstance(msg['ne_lat'], float)
          assert isinstance(msg['sw_lon'], float)
          assert isinstance(msg['sw_lat'], float)
  
      def test_msg_type_22_addressed(self):
          # Addressed
          msg = decode(b"!AIVDM,1,1,,A,F@@W>gOP00PH=JrN9l000?wB2HH;,0*44").asdict()
          assert msg['msg_type'] == 22
          assert msg['mmsi'] == "017419965"
          assert msg['channel_a'] == 3584
          assert msg['channel_b'] == 8
          assert msg['power'] == 1
          assert msg['addressed'] == 1
  
          assert msg['dest1'] == "028144881"
          assert msg['dest2'] == "268435519"
  
          assert msg['band_a'] == 0
          assert msg['band_b'] == 0
          assert msg['zonesize'] == 4
  
          assert 'ne_lon' not in msg.keys()
          assert 'ne_lat' not in msg.keys()
          assert 'sw_lon' not in msg.keys()
          assert 'sw_lat' not in msg.keys()
  
      def test_msg_type_23(self):
          msg = decode(b"!AIVDM,1,1,,B,G02:Kn01R`sn@291nj600000900,2*12").asdict()
          assert msg['msg_type'] == 23
          assert msg['mmsi'] == "002268120"
          assert msg['ne_lon'] == 157.8
          assert msg['ship_type'] == ShipType.NotAvailable
          assert msg['ne_lat'] == 3064.2
          assert msg['sw_lon'] == 109.6
          assert msg['sw_lat'] == 3040.8
          assert msg['station_type'] == StationType.REGIONAL
          assert msg['txrx'] == TransmitMode.TXA_TXB_RXA_RXB
          assert msg['interval'] == 9
          assert msg['quiet'] == 0
  
      def test_msg_type_24(self):
          msg = decode(b"!AIVDM,1,1,,A,H52KMeDU653hhhi0000000000000,0*1A").asdict()
          assert msg['msg_type'] == 24
          assert msg['mmsi'] == "338091445"
          assert msg['partno'] == 1
          assert msg['ship_type'] == ShipType.PleasureCraft
          assert msg['vendorid'] == "FEC"
          assert msg['callsign'] == ""
          assert msg['to_bow'] == 0
          assert msg['to_stern'] == 0
          assert msg['to_port'] == 0
          assert msg['to_starboard'] == 0
  
      def test_msg_type_25_a(self):
          msg = decode(b"!AIVDM,1,1,,A,I6SWo?8P00a3PKpEKEVj0?vNP<65,0*73").asdict()
  
          assert msg['msg_type'] == 25
          assert msg['addressed']
          assert not msg['structured']
          assert msg['dest_mmsi'] == "134218384"
  
      def test_msg_type_25_b(self):
          msg = decode(b"!AIVDO,1,1,,A,I6SWo?<P00a00;Cwwwwwwwwwwww0,0*4A").asdict()
          assert msg == {
              'addressed': 1,
              'data': 1208925819614629174706112,
              'dest_mmsi': '134218384',
              'mmsi': '440006460',
              'repeat': 0,
              'structured': 1,
              'app_id': 45,
              'msg_type': 25
          }
  
      def test_msg_type_25_c(self):
          msg = decode(b"!AIVDO,1,1,,A,I6SWo?8P00a0003wwwwwwwwwwww0,0*35").asdict()
          assert msg == {
              'addressed': 1,
              'data': 1208925819614629174706112,
              'dest_mmsi': '134218384',
              'mmsi': '440006460',
              'repeat': 0,
              'structured': 0,
              'msg_type': 25
          }
  
      def test_msg_type_26_a(self):
          msg = decode(b"!AIVDM,1,1,,A,JB3R0GO7p>vQL8tjw0b5hqpd0706kh9d3lR2vbl0400,2*40").asdict()
          assert msg['msg_type'] == 26
          assert msg['addressed']
          assert msg['structured']
          assert msg['dest_mmsi'] == "838351848"
          assert msg['data'] == 0x332fc0a85c39e2c007006cf026c0f4882faad001000
  
      def test_msg_type_26_b(self):
          msg = decode(b"!AIVDM,1,1,,A,J0@00@370>t0Lh3P0000200H:2rN92,4*14").asdict()
          assert msg['msg_type'] == 26
          assert not msg['addressed']
          assert not msg['structured']
          assert msg['data'] == 0xc700ef007300e0000000080018282e9e24
  
      def test_msg_type_27(self):
          msg = decode(b"!AIVDM,1,1,,B,KC5E2b@U19PFdLbMuc5=ROv62<7m,0*16").asdict()
          assert msg['msg_type'] == 27
          assert msg['mmsi'] == "206914217"
          assert msg['accuracy'] == 0
          assert msg['raim'] == 0
          assert msg['status'] == NavigationStatus.NotUnderCommand
          assert msg['lon'] == 137.023333
          assert msg['lat'] == 4.84
          assert msg['speed'] == 57
          assert msg['course'] == 167
          assert msg['gnss'] == 0
  
      def test_broken_messages(self):
          # Undefined epfd
          assert decode(b"!AIVDM,1,1,,B,4>O7m7Iu@<9qUfbtm`vSnwvH20S8,0*46").asdict()['epfd'] == EpfdType.Undefined
  
      def test_multiline_message(self):
          # these messages caused issue #3
          msg_1_part_0 = b'!AIVDM,2,1,1,A,538CQ>02A;h?D9QC800pu8@T>0P4l9E8L0000017Ah:;;5r50Ahm5;C0,0*07'
          msg_1_part_1 = b'!AIVDM,2,2,1,A,F@V@00000000000,2*35'
  
          assert decode(msg_1_part_0, msg_1_part_1).to_json()
  
          msg_2_part_0 = b'!AIVDM,2,1,9,A,538CQ>02A;h?D9QC800pu8@T>0P4l9E8L0000017Ah:;;5r50Ahm5;C0,0*0F'
          msg_2_part_1 = b'!AIVDM,2,2,9,A,F@V@00000000000,2*3D'
  
          assert decode(msg_2_part_0, msg_2_part_1).to_json()
  
      def test_byte_stream(self):
          messages = [
              b'!AIVDM,2,1,1,A,538CQ>02A;h?D9QC800pu8@T>0P4l9E8L0000017Ah:;;5r50Ahm5;C0,0*07',
              b'!AIVDM,2,2,1,A,F@V@00000000000,2*35',
              b'!AIVDM,2,1,9,A,538CQ>02A;h?D9QC800pu8@T>0P4l9E8L0000017Ah:;;5r50Ahm5;C0,0*0F',
              b'!AIVDM,2,2,9,A,F@V@00000000000,2*3D',
          ]
          counter = 0
          for msg in ByteStream(messages):
              decoded = msg.decode().asdict()
              assert decoded['shipname'] == 'NORDIC HAMBURG'
              assert decoded['mmsi'] == "210035000"
              assert decoded
              counter += 1
          assert counter == 2
  
      def test_empty_channel(self):
          msg = b"!AIVDO,1,1,,,B>qc:003wk?8mP=18D3Q3wgTiT;T,0*13"
  
          self.assertEqual(NMEAMessage(msg).channel, "")
  
          content = decode(msg).asdict()
          self.assertEqual(content["msg_type"], 18)
          self.assertEqual(content["repeat"], 0)
          self.assertEqual(content["mmsi"], "1000000000")
          self.assertEqual(content["speed"], 102.3)
          self.assertEqual(content["accuracy"], 0)
          self.assertEqual(str(content["lon"]), "181.0")
          self.assertEqual(str(content["lat"]), "91.0")
          self.assertEqual(str(content["course"]), "360.0")
          self.assertEqual(content["heading"], 511)
          self.assertEqual(content["second"], 31)
          self.assertEqual(content["reserved_2"], 0)
          self.assertEqual(content["cs"], 1)
          self.assertEqual(content["display"], 0)
          self.assertEqual(content["band"], 1)
          self.assertEqual(content["radio"], 410340)
  
      def test_msg_with_more_that_82_chars_payload(self):
          content = decode(
              "!AIVDM,1,1,,B,53ktrJ82>ia4=50<0020<5=@Dhv0t8T@u<0000001PV854Si0;mR@CPH13p0hDm1C3h0000,2*35"
          ).asdict()
  
          self.assertEqual(content["msg_type"], 5)
          self.assertEqual(content["mmsi"], "255801960")
          self.assertEqual(content["repeat"], 0)
          self.assertEqual(content["ais_version"], 2)
          self.assertEqual(content["imo"], 9356945)
          self.assertEqual(content["callsign"], "CQPC")
          self.assertEqual(content["shipname"], "CASTELO OBIDOS")
          self.assertEqual(content["ship_type"], ShipType.NotAvailable)
          self.assertEqual(content["to_bow"], 12)
          self.assertEqual(content["to_stern"], 38)
          self.assertEqual(content["to_port"], 8)
          self.assertEqual(content["to_starboard"], 5)
          self.assertEqual(content["epfd"], EpfdType.GPS)
          self.assertEqual(content["month"], 2)
          self.assertEqual(content["day"], 7)
          self.assertEqual(content["hour"], 17)
          self.assertEqual(content["minute"], 0)
          self.assertEqual(content["draught"], 4.7)
          self.assertEqual(content["destination"], "VIANA DO CASTELO")
  
      def test_nmea_decode(self):
          nmea = NMEAMessage(b"!AIVDO,1,1,,,B>qc:003wk?8mP=18D3Q3wgTiT;T,0*13")
          decoded = nmea.decode()
          assert decoded.msg_type == 18
          assert isinstance(decoded, MessageType18)
  
      def test_nmea_decode_unknown_msg(self):
          with self.assertRaises(UnknownMessageException):
              nmea = NMEAMessage(b"!AIVDO,1,1,,,B>qc:003wk?8mP=18D3Q3wgTiT;T,0*13")
              nmea.ais_id = 28
              nmea.decode()
  
      def test_decode_out_of_order(self):
          parts = [
              b"!AIVDM,2,2,4,A,000000000000000,2*20",
              b"!AIVDM,2,1,4,A,55O0W7`00001L@gCWGA2uItLth@DqtL5@F22220j1h742t0Ht0000000,0*08",
          ]
  
          decoded = decode(*parts)
  
          assert decoded.asdict()['mmsi'] == '368060190'
  
      def test_issue_46_a(self):
          msg = b"!ARVDM,2,1,3,B,E>m1c1>9TV`9WW@97QUP0000000F@lEpmdceP00003b,0*5C"
          decoded = NMEAMessage(msg).decode()
  
          self.assertEqual(decoded.msg_type, 21)
          self.assertEqual(decoded.repeat, 0)
          self.assertEqual(decoded.mmsi, '995126020')
          self.assertEqual(decoded.aid_type, NavAid.ISOLATED_DANGER)
          self.assertEqual(decoded.name, 'SIMPSON ROCK')
          self.assertEqual(decoded.accuracy, True)
          self.assertEqual(decoded.lon, 175.119987)
          self.assertEqual(decoded.lat, -36.0075)
          self.assertEqual(decoded.to_bow, 0)
          self.assertEqual(decoded.to_stern, 0)
          self.assertEqual(decoded.to_port, 0)
          self.assertEqual(decoded.to_starboard, 0)
          self.assertEqual(decoded.epfd, EpfdType.Surveyed)
          self.assertEqual(decoded.second, 10)
  
          # The following fields are None
          self.assertIsNone(decoded.off_position)
          self.assertIsNone(decoded.regional)
          self.assertIsNone(decoded.raim)
          self.assertIsNone(decoded.virtual_aid)
          self.assertIsNone(decoded.assigned)
          self.assertIsNone(decoded.name_ext)
  
      def test_issue_46_b(self):
          msg = b"!AIVDM,1,1,,B,E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@,2*52"
          decoded = NMEAMessage(msg).decode()
  
          self.assertEqual(decoded.msg_type, 21)
          self.assertEqual(decoded.repeat, 0)
          self.assertEqual(decoded.mmsi, '995036013')
          self.assertEqual(decoded.aid_type, NavAid.STARBOARD_HAND_MARK)
          self.assertEqual(decoded.name, 'STDB CUT 2')
          self.assertEqual(decoded.accuracy, True)
          self.assertEqual(decoded.lon, 115.691833)
          self.assertEqual(decoded.lat, -32.004333)
          self.assertEqual(decoded.to_bow, 0)
          self.assertEqual(decoded.to_stern, 0)
          self.assertEqual(decoded.to_port, 0)
          self.assertEqual(decoded.to_starboard, 0)
          self.assertEqual(decoded.epfd, EpfdType.Surveyed)
          self.assertEqual(decoded.second, 60)
          self.assertEqual(decoded.off_position, False)
          self.assertEqual(decoded.regional, 4)
  
          # The following fields are None
          self.assertIsNone(decoded.raim)
          self.assertIsNone(decoded.virtual_aid)
          self.assertIsNone(decoded.assigned)
          self.assertIsNone(decoded.name_ext)
  
      def test_msg_too_short_enum_is_none(self):
          msg = b"!AIVDM,1,1,,B,E>lt;,2*52"
          decoded = NMEAMessage(msg).decode()
  
          self.assertEqual(decoded.msg_type, 21)
          self.assertEqual(decoded.repeat, 0)
          self.assertEqual(decoded.mmsi, '000971714')
          self.assertIsNone(decoded.aid_type)
          self.assertIsNone(decoded.epfd)
  
          msg = b"!AIVDM,1,1,,B,15M6,0*5C"
          decoded = NMEAMessage(msg).decode()
          self.assertIsNone(decoded.maneuver)
  
          msg = b"!AIVDM,2,1,1,A,55?MbV02;H,0*00"
          decoded = NMEAMessage(msg).decode()
          self.assertIsNone(decoded.ship_type)
          self.assertIsNone(decoded.epfd)
  
      def test_to_dict_non_enum(self):
          """Enum types do not use None if the fields are missing when partial decoding"""
          msg = b"!AIVDM,1,1,,B,E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@,2*52"
          decoded = NMEAMessage(msg).decode()
  
          d = decoded.asdict(enum_as_int=True)
          self.assertEqual(
              d, {'accuracy': True,
                  'aid_type': 25,
                  'assigned': None,
                  'epfd': 7,
                  'lat': -32.004333,
                  'lon': 115.691833,
                  'mmsi': '995036013',
                  'msg_type': 21,
                  'name_ext': None,
                  'off_position': False,
                  'raim': None,
                  'regional': 4,
                  'repeat': 0,
                  'second': 60,
                  'name': 'STDB CUT 2',
                  'spare': None,
                  'to_bow': 0,
                  'to_port': 0,
                  'to_starboard': 0,
                  'to_stern': 0,
                  'virtual_aid': None}
          )
  
      def test_decode_and_merge(self):
          msg = b"!AIVDM,1,1,,B,E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@,2*52"
          decoded = NMEAMessage(msg)
  
          d = decoded.decode_and_merge(enum_as_int=True)
  
          self.assertEqual(
              d, {'accuracy': True,
                  'aid_type': 25,
                  'ais_id': 21,
                  'assigned': None,
                  'channel': 'B',
                  'checksum': 82,
                  'epfd': 7,
                  'fill_bits': 2,
                  'frag_cnt': 1,
                  'frag_num': 1,
                  'lat': -32.004333,
                  'lon': 115.691833,
                  'mmsi': '995036013',
                  'msg_type': 21,
                  'name_ext': None,
                  'off_position': False,
                  'payload': 'E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@',
                  'raim': None,
                  'raw': '!AIVDM,1,1,,B,E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@,2*52',
                  'regional': 4,
                  'repeat': 0,
                  'second': 60,
                  'seq_id': None,
                  'name': 'STDB CUT 2',
                  'spare': None,
                  'talker': 'AI',
                  'to_bow': 0,
                  'to_port': 0,
                  'to_starboard': 0,
                  'to_stern': 0,
                  'type': 'VDM',
                  'virtual_aid': None}
          )
  
          d = decoded.decode_and_merge(enum_as_int=False)
          self.assertEqual(
              d, {'accuracy': True,
                  'aid_type': NavAid.STARBOARD_HAND_MARK,
                  'ais_id': 21,
                  'assigned': None,
                  'channel': 'B',
                  'checksum': 82,
                  'epfd': EpfdType.Surveyed,
                  'fill_bits': 2,
                  'frag_cnt': 1,
                  'frag_num': 1,
                  'lat': -32.004333,
                  'lon': 115.691833,
                  'mmsi': '995036013',
                  'msg_type': 21,
                  'name_ext': None,
                  'off_position': False,
                  'payload': 'E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@',
                  'raim': None,
                  'raw': '!AIVDM,1,1,,B,E>lt;KLab21@1bb@I@@@@@@@@@@D8k2tnmvs000003v0@,2*52',
                  'regional': 4,
                  'repeat': 0,
                  'second': 60,
                  'seq_id': None,
                  'name': 'STDB CUT 2',
                  'spare': None,
                  'talker': 'AI',
                  'to_bow': 0,
                  'to_port': 0,
                  'to_starboard': 0,
                  'to_stern': 0,
                  'type': 'VDM',
                  'virtual_aid': None}
          )
  
      def test_issue_50(self):
          """Refer to PR: https://github.com/M0r13n/pyais/pull/50/files"""
          msg = MessageType5.create(mmsi="123456", ship_type=None, epfd=None)
  
          dictionary = msg.asdict(enum_as_int=True)
  
          self.assertIsNone(dictionary['epfd'])
          self.assertIsNone(dictionary['ship_type'])
  
      def test_none_value_converter_for_creation(self):
          """Make sure that None values can be encoded -> left out"""
          msg = MessageType6.create(mmsi="123456", dest_mmsi=None, data=None)
          self.assertIsNone(msg.data)
  
      def test_none_value_converter_for_decoding(self):
          """Make sure that None values do not cause any problems when decoding"""
          encoded = encode_dict({"mmsi": "123456", "dest_mmsi": None, "data": None, 'msg_type': 6})
          encoded = encoded[0]
          decoded = decode(encoded)
          self.assertIsNone(decoded.data)
  
      def test_none_values_converter_for_all_messages(self):
          """
          Create the shortest possible message that could potentially occur and try to decode it again.
          This is done to ensure, that there are no hiccups when trying to decode very short messages.
          """
          for mtype in range(28):
              cls = MSG_CLASS[mtype]
              fields = set(f.name for f in cls.fields())
              payload = {f: None for f in fields}
              payload['mmsi'] = 1337
              payload['msg_type'] = mtype
              encoded = encode_dict(payload)
  
              self.assertIsNotNone(encoded)
  
              decoded = decode(*encoded)
  
              for field in fields:
                  val = getattr(decoded, field)
                  if field == 'mmsi':
                      self.assertEqual(val, '000001337')
                  elif field == 'msg_type':
                      self.assertEqual(val, mtype)
                  elif field == 'repeat':
                      self.assertEqual(val, 0)
                  else:
                      self.assertIsNone(val)
  
      def test_type_25_very_short(self):
          """If the message is very short, an IndexError might o occur"""
          short_msg = b'!AIVDO,1,1,,A,Ig,0*65'
          decoded = decode(short_msg)
  
          self.assertEqual(decoded.mmsi, '000000015')
  
      def test_type_26_very_short(self):
          """If the message is very short, an IndexError might occur"""
          short_msg = b'!AIVDO,1,1,,A,Jgg,4*4E'
          decoded = decode(short_msg)
  
          self.assertEqual(decoded.mmsi, '000000062')
  
      def test_type_22_very_short(self):
          """If the mssage is very short an IndexError might occur"""
          short_msg = b'!AIVDO,1,1,,A,F0001,0*74'
          decoded = decode(short_msg)
  
          self.assertEqual(decoded.mmsi, '000000001')
  
      def test_types_for_messages(self):
          """Make sure that the types are consistent for all messages"""
          types = {}
          for typ, msg in MSG_CLASS.items():
              for field in msg.fields():
                  d_type = field.metadata['d_type']
                  f_name = field.name
                  if f_name in types:
                      if typ == 9 and f_name == 'speed' and d_type == int:
                          continue
                      if f_name == 'spare':
                          continue
                      if typ == 27 and f_name == 'speed' and d_type == int:
                          continue
                      if typ == 27 and f_name == 'course' and d_type == int:
                          continue
                      assert d_type == types[f_name], f"{typ}.{f_name}: {d_type} vs. {types[f_name]}"
                  else:
                      types[f_name] = d_type
  
          pprint(types)
  