Repository: M0r13n/pyais

Issue:
lat lon converters float accuracy
for the example sentence:
!AIVDM,1,1,,A,100u3g@0291Q1>BW6uDUwDk00LE@,0*74

decode and then encode will results in a change to lat lon

the problem line is:
```
            elif d_type == float:
                val = int(val)
                bits = int_to_bin(val, width, signed=signed)
```

Suggested resolution:
```
            elif d_type == float:
                val = int(round(val))
                bits = int_to_bin(val, width, signed=signed)
```

Kind Regards


Comments:

Patch:
diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index ca7d803..7e46af0 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,6 +1,11 @@
 ====================
  pyais CHANGELOG
 ====================
+-------------------------------------------------------------------------------
+ Version 2.5.1 26 Feb 2023
+-------------------------------------------------------------------------------
+* closes https://github.com/M0r13n/pyais/issues/107
+  * fixes a rounding error for lat/lon values
 -------------------------------------------------------------------------------
  Version 2.5.0 14 Jan 2023
 -------------------------------------------------------------------------------
diff --git a/README.md b/README.md
index eda8c0d..08dc62b 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
 [![Documentation Status](https://readthedocs.org/projects/pyais/badge/?version=latest)](https://pyais.readthedocs.io/en/latest/?badge=latest)
 
 AIS message encoding and decoding. 100% pure Python. Supports AIVDM/AIVDO messages. Supports single messages, files and
-TCP/UDP sockets.
+TCP/UDP sockets. This library has been used and tested extensively in representative real-world scenarios. This includes tests with live feeds from [Spire](https://spire.com/maritime/), the [Norwegian Coastal Administration](https://kystverket.no/navigasjonstjenester/ais/tilgang-pa-ais-data/) and others. I test each major release against a selection of public and non-public data sources to ensure the broadest possible compatibility.
 
 You can find the full documentation on [readthedocs](https://pyais.readthedocs.io/en/latest/).
 
diff --git a/pyais/__init__.py b/pyais/__init__.py
index 6c0568e..48f0804 100644
--- a/pyais/__init__.py
+++ b/pyais/__init__.py
@@ -5,7 +5,7 @@ from pyais.decode import decode
 from pyais.tracker import AISTracker, AISTrack
 
 __license__ = 'MIT'
-__version__ = '2.5.0'
+__version__ = '2.5.1'
 __author__ = 'Leon Morten Richter'
 
 __all__ = (
diff --git a/pyais/messages.py b/pyais/messages.py
index 527ef6c..b7616e8 100644
--- a/pyais/messages.py
+++ b/pyais/messages.py
@@ -742,7 +742,7 @@ def to_speed(v: typing.Union[int, float]) -> float:
 
 
 def from_lat_lon(v: typing.Union[int, float]) -> float:
-    return float(v) * 600000.0
+    return round(float(v) * 600000.0)
 
 
 def to_lat_lon(v: typing.Union[int, float]) -> float:
@@ -750,7 +750,7 @@ def to_lat_lon(v: typing.Union[int, float]) -> float:
 
 
 def from_lat_lon_600(v: typing.Union[int, float]) -> float:
-    return float(v) * 600.0
+    return round(float(v) * 600.0)
 
 
 def to_lat_lon_600(v: typing.Union[int, float]) -> float:


Test patch:
diff --git a/tests/test_decode.py b/tests/test_decode.py
index b4d6c32..cea212b 100644
--- a/tests/test_decode.py
+++ b/tests/test_decode.py
@@ -6,7 +6,7 @@ import textwrap
 import typing
 import unittest
 
-from pyais import NMEAMessage, encode_dict
+from pyais import NMEAMessage, encode_dict, encode_msg
 from pyais.ais_types import AISType
 from pyais.constants import (EpfdType, ManeuverIndicator, NavAid,
                              NavigationStatus, ShipType, StationType, SyncState,
@@ -1542,3 +1542,16 @@ class TestAIS(unittest.TestCase):
 
         with self.assertRaises(UnknownMessageException):
             decode_nmea_line(b",n:4,r:35435435435,foo bar 200")
+
+    def test_that_lat_and_long_are_rounded_correctly(self):
+        """Original Issue: https://github.com/M0r13n/pyais/issues/107
+        TL;DR: There was a rounding issue with certain values for lat and lon.
+        Decoding, encoding and then decoding again led to slight changes to lat/lon."""
+
+        orig = '!AIVDM,1,1,,A,100u3g@0291Q1>BW6uDUwDk00LE@,0*74'
+
+        first_decode = decode(orig)
+        encoded = encode_msg(first_decode)[0]
+        second_decode = decode(encoded)
+
+        self.assertEqual(first_decode, second_decode)
diff --git a/tests/test_tag_block.py b/tests/test_tag_block.py
index 2f19462..bc107e2 100644
--- a/tests/test_tag_block.py
+++ b/tests/test_tag_block.py
@@ -35,6 +35,29 @@ class TagBlockTestCase(unittest.TestCase):
         self.assertEqual(tb.relative_time, None)
         self.assertEqual(tb.text, None)
 
+    def test_spire_maritime_format(self):
+        """https://documentation.spire.com/tcp-stream-v2/the-nmea-message-encoding-format/"""
+        text = textwrap.dedent("""
+        \\c:1503079517*55\\!AIVDM,1,1,,B,C6:b0Kh09b3t1K4ChsS2FK008NL>`2CT@2N000000000S4h8S400,0*50
+        \\c:1503079517*53\\!AIVDM,1,1,,B,16:Vk1h00g8O=vRBDhNp0nKp0000,0*40
+        \\c:1503079517*53\\!AIVDM,1,1,,B,18155hh00u0DEU`N1F@Bg22R06@D,0*60
+        \\c:1503079517*53\\!AIVDM,1,1,,A,83aGFQ@j2ddtMH1b@g?b`7mL0,0*55
+        \\c:1503079517*53\\!AIVDM,2,1,9,A,53m@FJ400000hT5<0008E8q@TpF000000000000T2P3425rg0:53kThQDQh0,0*48
+        \\c:1503079517*53\\!AIVDM,2,2,9,A,00000000000,2*2D
+        \\c:1503079517*52\\!AIVDM,1,1,,A,13oP50Oi420UAtPgp@UPrP1d01,0*1A
+        \\c:1503079517*52\\!AIVDM,1,1,,B,B3mISo000H;wsB8SetMnww`5oP06,0*7C
+        \\c:1503079517*53\\!AIVDM,2,1,0,B,53aIjwh000010CSK7R04lu8F222222222222221?9@<297?o060@C51D`888,0*1B
+        """)
+
+        messages = [line.encode() for line in text.split() if line]
+
+        with IterMessages(messages) as s:
+            for msg in s:
+                msg.tag_block.init()
+                decoded = msg.decode()
+                self.assertIsNotNone(decoded.mmsi)
+                self.assertEqual(msg.tag_block.receiver_timestamp, '1503079517')
+
     def test_multiple_messages(self):
         text = textwrap.dedent("""
         \\s:2573535,c:1671533231*08\\!BSVDM,2,2,8,B,00000000000,2*36