Skip to content

Commit

Permalink
stronger type hints + ByteStream
Browse files Browse the repository at this point in the history
  • Loading branch information
M0r13n committed Jul 22, 2020
1 parent e2083f0 commit 73aa568
Show file tree
Hide file tree
Showing 12 changed files with 173 additions and 59 deletions.
11 changes: 8 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
init:
pip install -r requirements.txt
pip install -r requirements_dev.txt

test:
run_tests:
nosetests -v --with-coverage --cover-package pyais tests/*.py

flake:
python -m flake8

build:
rm -rf dist/ && python setup.py sdist bdist_wheel

check-build:
twine check dist/*
twine check dist/*

test: run_tests flake
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# pyais
[![PyPI](https://img.shields.io/pypi/v/pyais)](https://pypi.org/project/pyais/)
[![license](https://img.shields.io/pypi/l/pyais)](https://github.com/M0r13n/pyais/blob/master/LICENSE)
[![codecov](https://codecov.io/gh/M0r13n/pyais/branch/master/graph/badge.svg)](https://codecov.io/gh/M0r13n/pyais)
[![downloads](https://img.shields.io/pypi/dm/pyais)](https://pypi.org/project/pyais/)

AIS message decoding. 100% pure Python. Supports AIVDM/AIVDO messages.

Expand Down
2 changes: 1 addition & 1 deletion pyais/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from pyais.stream import TCPStream, FileReaderStream

__license__ = 'MIT'
__version__ = '1.0.0'
__version__ = '1.0.1'

__all__ = (
'NMEAMessage',
Expand Down
6 changes: 3 additions & 3 deletions pyais/ais_types.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from enum import Enum
from enum import IntEnum


class AISType(Enum):
class AISType(IntEnum):
# Refer to https://gpsd.gitlab.io/gpsd/AIVDM.html
NOT_IMPLEMENTED = 0
POS_CLASS_A1 = 1
POS_CLASS_A2 = 2
POS_CLASS_A3 = 3
Expand Down Expand Up @@ -30,7 +31,6 @@ class AISType(Enum):
BINARY_SINGLE_SLOT = 25
BINARY_MULTI_SLOT = 26
LONG_RANGE_BROADCAST = 27
NOT_IMPLEMENTED = 0

@classmethod
def _missing_(cls, value):
Expand Down
85 changes: 50 additions & 35 deletions pyais/decode.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from functools import partial
from pyais.exceptions import UnknownMessageException
import typing

import bitarray

Expand All @@ -15,7 +17,7 @@
from pyais.util import get_int, encode_bin_as_ascii6


def decode_msg_1(bit_arr: bitarray) -> dict:
def decode_msg_1(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a
Expand All @@ -40,22 +42,22 @@ def decode_msg_1(bit_arr: bitarray) -> dict:
}


def decode_msg_2(bit_arr: bitarray) -> dict:
def decode_msg_2(bit_arr: bitarray.bitarray) -> typing.Dict:
"""AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a
"""
return decode_msg_1(bit_arr)


def decode_msg_3(bit_arr: bitarray) -> dict:
def decode_msg_3(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
AIS Vessel position report using ITDMA (Incremental Time Division Multiple Access)
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_types_1_2_and_3_position_report_class_a
"""
return decode_msg_1(bit_arr)


def decode_msg_4(bit_arr: bitarray) -> dict:
def decode_msg_4(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
AIS Vessel position report using SOTDMA (Self-Organizing Time Division Multiple Access)
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_4_base_station_report
Expand All @@ -80,7 +82,7 @@ def decode_msg_4(bit_arr: bitarray) -> dict:
}


def decode_msg_5(bit_arr: bitarray) -> dict:
def decode_msg_5(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Static and Voyage Related Data
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_5_static_and_voyage_related_data
Expand Down Expand Up @@ -110,7 +112,7 @@ def decode_msg_5(bit_arr: bitarray) -> dict:
}


def decode_msg_6(bit_arr: bitarray) -> dict:
def decode_msg_6(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Binary Addresses Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_4_base_station_report
Expand All @@ -129,7 +131,7 @@ def decode_msg_6(bit_arr: bitarray) -> dict:
}


def decode_msg_7(bit_arr: bitarray) -> dict:
def decode_msg_7(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Binary Acknowledge
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_7_binary_acknowledge
Expand All @@ -150,7 +152,7 @@ def decode_msg_7(bit_arr: bitarray) -> dict:
}


def decode_msg_8(bit_arr: bitarray) -> dict:
def decode_msg_8(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Binary Acknowledge
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_8_binary_broadcast_message
Expand All @@ -166,7 +168,7 @@ def decode_msg_8(bit_arr: bitarray) -> dict:
}


def decode_msg_9(bit_arr: bitarray) -> dict:
def decode_msg_9(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Standard SAR Aircraft Position Report
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_9_standard_sar_aircraft_position_report
Expand All @@ -190,7 +192,7 @@ def decode_msg_9(bit_arr: bitarray) -> dict:
}


def decode_msg_10(bit_arr: bitarray) -> dict:
def decode_msg_10(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
UTC/Date Inquiry
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_10_utc_date_inquiry
Expand All @@ -204,15 +206,15 @@ def decode_msg_10(bit_arr: bitarray) -> dict:
}


def decode_msg_11(bit_arr: bitarray) -> dict:
def decode_msg_11(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
UTC/Date Response
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_11_utc_date_response
"""
return decode_msg_4(bit_arr)


def decode_msg_12(bit_arr: bitarray) -> dict:
def decode_msg_12(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Addressed Safety-Related Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_12_addressed_safety_related_message
Expand All @@ -229,14 +231,14 @@ def decode_msg_12(bit_arr: bitarray) -> dict:
}


def decode_msg_13(bit_arr: bitarray) -> dict:
def decode_msg_13(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Identical to type 7
"""
return decode_msg_7(bit_arr)


def decode_msg_14(bit_arr: bitarray) -> dict:
def decode_msg_14(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Safety-Related Broadcast Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_14_safety_related_broadcast_message
Expand All @@ -250,7 +252,7 @@ def decode_msg_14(bit_arr: bitarray) -> dict:
}


def decode_msg_15(bit_arr: bitarray) -> dict:
def decode_msg_15(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Interrogation
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_15_interrogation
Expand All @@ -271,7 +273,7 @@ def decode_msg_15(bit_arr: bitarray) -> dict:
}


def decode_msg_16(bit_arr: bitarray) -> dict:
def decode_msg_16(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Assignment Mode Command
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_16_assignment_mode_command
Expand All @@ -290,7 +292,7 @@ def decode_msg_16(bit_arr: bitarray) -> dict:
}


def decode_msg_17(bit_arr: bitarray) -> dict:
def decode_msg_17(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
DGNSS Broadcast Binary Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_17_dgnss_broadcast_binary_message
Expand All @@ -306,7 +308,7 @@ def decode_msg_17(bit_arr: bitarray) -> dict:
}


def decode_msg_18(bit_arr: bitarray) -> dict:
def decode_msg_18(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Standard Class B CS Position Report
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_18_standard_class_b_cs_position_report
Expand Down Expand Up @@ -335,7 +337,7 @@ def decode_msg_18(bit_arr: bitarray) -> dict:
}


def decode_msg_19(bit_arr: bitarray) -> dict:
def decode_msg_19(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Extended Class B CS Position Report
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_19_extended_class_b_cs_position_report
Expand Down Expand Up @@ -366,7 +368,7 @@ def decode_msg_19(bit_arr: bitarray) -> dict:
}


def decode_msg_20(bit_arr: bitarray) -> dict:
def decode_msg_20(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Data Link Management Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_20_data_link_management_message
Expand Down Expand Up @@ -399,7 +401,7 @@ def decode_msg_20(bit_arr: bitarray) -> dict:
}


def decode_msg_21(bit_arr: bitarray) -> dict:
def decode_msg_21(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Aid-to-Navigation Report
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_21_aid_to_navigation_report
Expand Down Expand Up @@ -433,7 +435,7 @@ def decode_msg_21(bit_arr: bitarray) -> dict:
}


def decode_msg_22(bit_arr: bitarray) -> dict:
def decode_msg_22(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Channel Management
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_22_channel_management
Expand Down Expand Up @@ -473,7 +475,7 @@ def decode_msg_22(bit_arr: bitarray) -> dict:
return data


def decode_msg_23(bit_arr: bitarray) -> dict:
def decode_msg_23(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Group Assignment Command
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_23_group_assignment_command
Expand All @@ -497,26 +499,27 @@ def decode_msg_23(bit_arr: bitarray) -> dict:
}


def decode_msg_24(bit_arr: bitarray) -> dict:
def decode_msg_24(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Static Data Report
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_24_static_data_report
"""
get_int_from_data = partial(get_int, bit_arr)
data = {
data: typing.Dict = {
'type': get_int_from_data(0, 6),
'repeat': get_int_from_data(8, 8),
'mmsi': get_int_from_data(8, 38),
'partno': get_int_from_data(38, 40)
}
# Part A

if not data['partno']:
d = {
# Part A
d: typing.Dict = {
'shipname': encode_bin_as_ascii6(bit_arr[40: 160])
}
# Part B
else:
d = {
# Part B
d: typing.Dict = {
'shiptype': ShipType(get_int_from_data(40, 48)),
'vendorid': encode_bin_as_ascii6(bit_arr[48: 66]),
'model': get_int_from_data(66, 70),
Expand All @@ -532,7 +535,7 @@ def decode_msg_24(bit_arr: bitarray) -> dict:
return data


def decode_msg_25(bit_arr: bitarray) -> dict:
def decode_msg_25(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Single Slot Binary Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_25_single_slot_binary_message
Expand Down Expand Up @@ -573,7 +576,7 @@ def decode_msg_25(bit_arr: bitarray) -> dict:
return data


def decode_msg_26(bit_arr: bitarray) -> dict:
def decode_msg_26(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Multiple Slot Binary Message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_26_multiple_slot_binary_message
Expand Down Expand Up @@ -617,7 +620,7 @@ def decode_msg_26(bit_arr: bitarray) -> dict:
return data


def decode_msg_27(bit_arr: bitarray) -> dict:
def decode_msg_27(bit_arr: bitarray.bitarray) -> typing.Dict:
"""
Long Range AIS Broadcast message
Src: https://gpsd.gitlab.io/gpsd/AIVDM.html#_type_27_long_range_ais_broadcast_message
Expand Down Expand Up @@ -672,8 +675,20 @@ def decode_msg_27(bit_arr: bitarray) -> dict:
]


def decode(msg):
if msg.is_valid and 0 < msg.ais_id < 28:
def _decode(msg) -> typing.Dict:
"""
Decodes a given NMEA message.
"""
try:
return DECODE_MSG[msg.ais_id](msg.bit_array)
except IndexError as e:
raise UnknownMessageException(f"The message {msg} is not currently supported!") from e


return None
def decode(msg) -> typing.Dict:
"""
Decodes a given message.
@param msg: A object of type NMEAMessage to decode
"""
return _decode(msg)
7 changes: 7 additions & 0 deletions pyais/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
class InvalidNMEAMessageException(Exception):
"""Invalid NMEA Message"""
pass


class InvalidChecksumException(Exception):
"""Invalid Checksum"""
pass


class UnknownMessageException(Exception):
"""Message not supported yet"""
pass
Loading

0 comments on commit 73aa568

Please sign in to comment.