Skip to content

Commit

Permalink
Correctly limit the maximum message size and allow empty channels (#29)
Browse files Browse the repository at this point in the history
  • Loading branch information
M0r13n authored May 8, 2021
1 parent 3df1179 commit 7f46e35
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 11 deletions.
22 changes: 22 additions & 0 deletions docs/conversion.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
############
Conversion
############

The following fields are directly converted to floats without **ANY** conversion:

1. Turn
2. Speed (speed over ground)
3. Longitude
4. Latitude
5. Course (Course over ground)
6. `to_bow`, `to_stern`, `to_port`, `to_starboard`
7. `ne_lon`, `ne_lat`, `sw_lon`, `sw_lat`

All of these values are native ``floats``. This means that you need to convert the value into the format of choice.

A common use case is to convert the values into strings, with fixed sized precision::

content = decode_msg("!AIVDO,1,1,,,B>qc:003wk?8mP=18D3Q3wgTiT;T,0*13")
print(content["speed"]) #=> 102.30000000000001
print(format(content["speed"], ".1f")) #=> 102.3
print(f"{content['speed'] :.6f}") #=> 102.300000
5 changes: 3 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
############
################
Usage examples
############
################

.. toctree::
:maxdepth: 1

examples/single
examples/file
examples/sockets
conversion
11 changes: 3 additions & 8 deletions pyais/messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,6 @@ def validate_message(msg: bytes) -> None:
"Sentence number is empty!"
)

if not values[4]:
raise InvalidNMEAMessageException(
"The AIS channel (A or B) is empty."
)

if not values[5]:
raise InvalidNMEAMessageException(
"The NMEA message body (payload) is empty."
Expand Down Expand Up @@ -95,10 +90,10 @@ def validate_message(msg: bytes) -> None:
"Invalid sequential message ID. No Number."
)

# It should not have more than 82 chars (including starting $ or ! and <LF>)
if len(msg) > 82:
# It should not have more than 82 chars of payload
if len(values[5]) > 82:
raise InvalidNMEAMessageException(
f"{msg.decode('utf-8')} has more than 82 characters."
f"{msg.decode('utf-8')} has more than 82 characters of payload."
)

# Only encapsulated messages are currently supported
Expand Down
48 changes: 48 additions & 0 deletions tests/test_ais.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import unittest

from pyais import decode_msg
from pyais.ais_types import AISType
from pyais.constants import ManeuverIndicator, NavigationStatus, ShipType, NavAid, EpfdType
from pyais.exceptions import UnknownMessageException
Expand Down Expand Up @@ -583,3 +584,50 @@ def test_fail_silently(self):
"decoded": {}
}"""
self.assertEqual(nmea.decode().to_json(), text)

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(msg)
self.assertEqual(content["type"], 18)
self.assertEqual(content["repeat"], 0)
self.assertEqual(content["mmsi"], "1000000000")
self.assertEqual(format(content["speed"], ".1f"), "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["regional"], 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_msg(
"!AIVDM,1,1,,B,53ktrJ82>ia4=50<0020<5=@Dhv0t8T@u<0000001PV854Si0;mR@CPH13p0hDm1C3h0000,2*35"
)

self.assertEqual(content["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["shiptype"], 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")
1 change: 0 additions & 1 deletion tests/test_decode_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def should_raise(msg):
should_raise(",1,1,,A,403Ovl@000Htt<tSF0l4Q@100`Pq,0*28")
should_raise("!AIVDM,,1,,A,403Ovl@000Htt<tSF0l4Q@100`Pq,0*28")
should_raise("!AIVDM,1,,,A,403Ovl@000Htt<tSF0l4Q@100`Pq,0*28")
should_raise("!AIVDM,1,1,,,403Ovl@000Htt<tSF0l4Q@100`Pq,0*28")
should_raise("!AIVDM,1,1,,A,,0*28")
should_raise("!AIVDM,1,1,,A,403Ovl@000Htt<tSF0l4Q@100`Pq,")

Expand Down

0 comments on commit 7f46e35

Please sign in to comment.