coveralls: 
[![Coverage Status](https://coveralls.io/builds/12922124/badge)](https://coveralls.io/builds/12922124)

Coverage increased (+0.1%) to 88.73% when pulling **64b21099e9a5aa15d452ae9e8c907b2741435b02 on fix-issue-60** into **a857f00fd7150818b4aa28f267c03a8c90a0dda5 on master**.

codecov-io: # [Codecov](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=h1) Report
> Merging [#61](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=desc) into [master](https://codecov.io/gh/discos/simulators/commit/a857f00fd7150818b4aa28f267c03a8c90a0dda5?src=pr&el=desc) will **increase** coverage by `0.13%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/discos/simulators/pull/61/graphs/tree.svg?src=pr&token=UgIM6Vs2q5&height=150&width=650)](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #61      +/-   ##
==========================================
+ Coverage   88.59%   88.73%   +0.13%     
==========================================
  Files           7        7              
  Lines        1245     1260      +15     
==========================================
+ Hits         1103     1118      +15     
  Misses        142      142
```


| [Impacted Files](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/test\_utils.py](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=tree#diff-dGVzdHMvdGVzdF91dGlscy5weQ==) | `100% <100%> (ø)` | :arrow_up: |
| [simulators/utils.py](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=tree#diff-c2ltdWxhdG9ycy91dGlscy5weQ==) | `93.22% <100%> (+0.62%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=footer). Last update [a857f00...4b168c3](https://codecov.io/gh/discos/simulators/pull/61?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12933700/badge)](https://coveralls.io/builds/12933700)

Coverage increased (+0.1%) to 88.73% when pulling **4b168c3aa1c4e8f4ba07ec5131dfdd0649389947 on fix-issue-60** into **a857f00fd7150818b4aa28f267c03a8c90a0dda5 on master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12936275/badge)](https://coveralls.io/builds/12936275)

Coverage increased (+0.2%) to 88.766% when pulling **d147cbaad39c5c813f01e7e0aaef1a8bf0f4deba on fix-issue-60** into **a857f00fd7150818b4aa28f267c03a8c90a0dda5 on master**.

diff --git a/simulators/utils.py b/simulators/utils.py
index f289e78..25e23ff 100644
--- a/simulators/utils.py
+++ b/simulators/utils.py
@@ -46,28 +46,38 @@ def twos_to_int(binary_string):
     return val
 
 
-def int_to_twos(val):
+def int_to_twos(val, n_bytes=4):
     """Return the two's complement of the given integer as a string of zeroes
-    and ones with len = 32.
+    and ones with len = 8*n_bytes.
 
     >>> int_to_twos(5)
     '00000000000000000000000000000101'
 
+    >>> int_to_twos(5, 2)
+    '0000000000000101'
+
     >>> int_to_twos(-1625)
     '11111111111111111111100110100111'
 
+    >>> int_to_twos(-1625, 2)
+    '1111100110100111'
+
     >>> int_to_twos(4294967295)
     Traceback (most recent call last):
         ...
     ValueError: 4294967295 out of range (-2147483648, 2147483647).
     """
-    if val < -2147483648 or val > 2147483647:
+    n_bits = 8 * n_bytes
+    min_range = -int(math.pow(2, n_bits - 1))
+    max_range = int(math.pow(2, n_bits - 1)) - 1
+
+    if val < min_range or val > max_range:
         raise ValueError(
             "%d out of range (%d, %d)."
-            % (val, -2147483648, 2147483647)
+            % (val, min_range, max_range)
         )
-    binary_string = bin(val & int("1" * 32, 2))[2:]
-    return ("{0:0>%s}" % 32).format(binary_string)
+    binary_string = bin(val & int("1" * n_bits, 2))[2:]
+    return ("{0:0>%s}" % n_bits).format(binary_string)
 
 
 def binary_to_bytes(binary_string):
@@ -85,6 +95,20 @@ def binary_to_bytes(binary_string):
     return byte_string
 
 
+def bytes_to_int(byte_string):
+    """Convert a string of bytes to an integer (like C atoi function).
+
+    >>> bytes_to_int(b'\x10\x05\xFA\xFF')
+    268827391
+    """
+    binary_string = ''
+
+    for char in byte_string:
+        binary_string += bin(ord(char))[2:].zfill(8)
+
+    return twos_to_int(binary_string)
+
+
 def mjd():
     """Return the modified julian date. https://bowie.gsfc.nasa.gov/time/"""
     utcnow = datetime.utcnow()

Add a method to convert a string of bytes into an integer (like C atoi function).
The method should be implemented in utils.py.
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 8569f6f..7d776a6 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -30,6 +30,10 @@ class TestServer(unittest.TestCase):
             utils.int_to_twos(5),
             '00000000000000000000000000000101'
         )
+        self.assertEqual(
+            utils.int_to_twos(5, 2),
+            '0000000000000101'
+        )
 
     def test_out_of_range_int_to_twos(self):
         """Raise a ValueError in case of out of range integer value"""
@@ -61,5 +65,18 @@ class TestServer(unittest.TestCase):
         expected_byte_string = b'\x05\x1A\x28\xD3'
         self.assertNotEqual(byte_string, expected_byte_string)
 
+    def test_bytes_to_int_correct(self):
+        """Convert a string of bytes into an integer (like C atoi function)"""
+        byte_string = b'\x00\x00\xFA\xFF'
+        result = utils.bytes_to_int(byte_string)
+        expected_result = 64255
+        self.assertEqual(result, expected_result)
+
+    def test_bytes_to_int_wrong(self):
+        byte_string = b'\x00\x00\xFA\xFF'
+        result = utils.bytes_to_int(byte_string)
+        expected_result = -1281
+        self.assertNotEqual(result, expected_result)
+
 if __name__ == '__main__':
     unittest.main()

pip install -e .[test]
parse_log_pytest
pytest
coverage prospector sphinx sphinx_rtd_theme tox pytest
apt-get update apt-get install -y gcc
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.16
astroid==3.3.9
babel==2.17.0
cachetools==5.5.2
certifi==2025.1.31
chardet==5.2.0
charset-normalizer==3.4.1
colorama==0.4.6
coverage==7.8.0
dill==0.3.9
-e git+https://github.com/discos/simulators.git@a857f00fd7150818b4aa28f267c03a8c90a0dda5#egg=discos_simulators
distlib==0.3.9
docutils==0.21.2
dodgy==0.2.1
exceptiongroup @ file:///croot/exceptiongroup_1706031385326/work
filelock==3.18.0
flake8==7.2.0
flake8-polyfill==1.0.2
gitdb==4.0.12
GitPython==3.1.44
idna==3.10
imagesize==1.4.1
importlib_metadata==8.6.1
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isort==6.0.1
Jinja2==3.1.6
MarkupSafe==3.0.2
mccabe==0.7.0
packaging @ file:///croot/packaging_1734472117206/work
pep8-naming==0.10.0
platformdirs==4.3.7
pluggy @ file:///croot/pluggy_1733169602837/work
prospector==1.16.1
pycodestyle==2.13.0
pydocstyle==6.3.0
pyflakes==3.3.2
Pygments==2.19.1
pylint==3.3.6
pylint-celery==0.3
pylint-django==2.6.1
pylint-plugin-utils==0.8.2
pyproject-api==1.9.0
pytest @ file:///croot/pytest_1738938843180/work
PyYAML==6.0.2
requests==2.32.3
requirements-detector==1.3.2
semver==3.0.4
setoptconf-tmp==0.3.1
smmap==5.0.2
snowballstemmer==2.2.0
Sphinx==7.4.7
sphinx-rtd-theme==3.0.2
sphinxcontrib-applehelp==2.0.0
sphinxcontrib-devhelp==2.0.0
sphinxcontrib-htmlhelp==2.1.0
sphinxcontrib-jquery==4.1
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
toml==0.10.2
tomli==2.2.1
tomlkit==0.13.2
tox==4.25.0
typing_extensions==4.13.0
urllib3==2.3.0
virtualenv==20.29.3
zipp==3.21.0

name: simulators
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - exceptiongroup=1.2.0=py39h06a4308_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - packaging=24.2=py39h06a4308_0
  - pip=25.0=py39h06a4308_0
  - pluggy=1.5.0=py39h06a4308_0
  - pytest=8.3.4=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.16
      - astroid==3.3.9
      - babel==2.17.0
      - cachetools==5.5.2
      - certifi==2025.1.31
      - chardet==5.2.0
      - charset-normalizer==3.4.1
      - colorama==0.4.6
      - coverage==7.8.0
      - dill==0.3.9
      - distlib==0.3.9
      - docutils==0.21.2
      - dodgy==0.2.1
      - filelock==3.18.0
      - flake8==7.2.0
      - flake8-polyfill==1.0.2
      - gitdb==4.0.12
      - gitpython==3.1.44
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==8.6.1
      - isort==6.0.1
      - jinja2==3.1.6
      - markupsafe==3.0.2
      - mccabe==0.7.0
      - pep8-naming==0.10.0
      - platformdirs==4.3.7
      - prospector==1.16.1
      - pycodestyle==2.13.0
      - pydocstyle==6.3.0
      - pyflakes==3.3.2
      - pygments==2.19.1
      - pylint==3.3.6
      - pylint-celery==0.3
      - pylint-django==2.6.1
      - pylint-plugin-utils==0.8.2
      - pyproject-api==1.9.0
      - pyyaml==6.0.2
      - requests==2.32.3
      - requirements-detector==1.3.2
      - semver==3.0.4
      - setoptconf-tmp==0.3.1
      - smmap==5.0.2
      - snowballstemmer==2.2.0
      - sphinx==7.4.7
      - sphinx-rtd-theme==3.0.2
      - sphinxcontrib-applehelp==2.0.0
      - sphinxcontrib-devhelp==2.0.0
      - sphinxcontrib-htmlhelp==2.1.0
      - sphinxcontrib-jquery==4.1
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==2.0.0
      - sphinxcontrib-serializinghtml==2.0.0
      - toml==0.10.2
      - tomli==2.2.1
      - tomlkit==0.13.2
      - tox==4.25.0
      - typing-extensions==4.13.0
      - urllib3==2.3.0
      - virtualenv==20.29.3
      - zipp==3.21.0
prefix: /opt/conda/envs/simulators

tests/test_utils.py::TestServer::test_int_to_twos
tests/test_utils.py::TestServer::test_binary_to_bytes_correct tests/test_utils.py::TestServer::test_binary_to_bytes_wrong tests/test_utils.py::TestServer::test_bytes_to_int_correct tests/test_utils.py::TestServer::test_bytes_to_int_wrong tests/test_utils.py::TestServer::test_day_milliseconds
tests/test_utils.py::TestServer::test_mjd tests/test_utils.py::TestServer::test_out_of_range_int_to_twos tests/test_utils.py::TestServer::test_right_checksum tests/test_utils.py::TestServer::test_right_twos_to_int tests/test_utils.py::TestServer::test_wrong_checksum tests/test_utils.py::TestServer::test_wrong_twos_to_int
simulators/utils.py
simulators/utils.py