27 Commits

Author SHA1 Message Date
Xavier Olive
070dc80bf4 include pyd files to wheel 2023-08-13 10:57:20 +02:00
Xavier Olive
8d9a8df9d9 minor fix 2023-07-18 13:47:06 +00:00
Xavier Olive
458a78c30b add the decoding of the gps status indicator 2023-07-18 13:42:16 +00:00
Junzi Sun
57aa7a7d9c update test 2023-07-18 13:41:54 +00:00
Junzi Sun
8e2051af68 update test 2023-05-22 17:05:43 +02:00
Paul de Jong
50864b56aa Remove rounding in pyModeS (#147)
* Update bds50.py: round roll to 3 places instead of 1

Provide a bit more detail for smaller roll angles.

* remove rounding

* update poetry

* update poetry

* update test

* update bds06

* update workflow

---------

Co-authored-by: Junzi Sun <junzisun@gmail.com>
2023-05-22 17:00:58 +02:00
dependabot[bot]
8403cb61e4 Bump actions/checkout from 2 to 3 (#145)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 14:45:28 +01:00
dependabot[bot]
a5b8d670d3 Bump actions/setup-python from 2 to 4 (#144)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-02 14:45:05 +01:00
Xavier Olive
4e19ecdfa9 minor changes, new release 2023-02-14 18:39:52 +01:00
Nicholas (Nick) Kruzan
0d7a310007 Update py_common.py (#143) 2023-02-12 13:20:42 +01:00
Xavier Olive
66c29840b0 fix build issues 2023-01-19 15:18:16 +01:00
Xavier Olive
99dc2a4f40 limit wheels to windows, otherwise build from src 2022-12-31 00:42:50 +01:00
Xavier Olive
39936e4472 fix publish 2022-12-30 19:53:33 +01:00
Xavier Olive
144ee1710d fix publish 2022-12-30 19:46:15 +01:00
Xavier Olive
09ed997f91 fix publish 2022-12-30 19:40:25 +01:00
Xavier Olive
e437931254 fix publish 2022-12-30 19:36:16 +01:00
Xavier Olive
72fcc794e7 clean dependencies 2022-12-30 19:22:10 +01:00
Xavier Olive
ff12d819da fix publish source 2022-12-30 14:17:36 +01:00
Xavier Olive
dc9d7ea91a fix test runner.os 2022-12-30 14:10:22 +01:00
Xavier Olive
5bc11cd78c fix test runner.os 2022-12-30 14:08:44 +01:00
Xavier Olive
9e6f9c3ffe upload source 2022-12-30 14:05:38 +01:00
dependabot[bot]
e1017bc2e5 Bump codecov/codecov-action from 2 to 3 (#136)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 2 to 3.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-12-30 14:02:05 +01:00
Xavier Olive
ebe1f76cd9 dependabot.yml 2022-12-30 13:57:57 +01:00
Xavier Olive
8830ef3dfe fix poetry PATH for windows 2022-12-30 13:50:43 +01:00
Xavier Olive
d999851da5 fix typo and && 2022-12-30 13:42:52 +01:00
Xavier Olive
6d0406c6b9 update version 2022-12-30 13:28:36 +01:00
Xavier Olive
6140f84058 update yml 2022-12-30 00:59:30 +01:00
20 changed files with 379 additions and 910 deletions

11
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "monthly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"

View File

@@ -1,29 +1,67 @@
# This workflows will upload a Python Package using Twine when a release is created
# For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries
name: PyPI Publish
name: publish
on:
release:
types: [created]
workflow_dispatch:
env:
POETRY_VERSION: "1.3.1"
jobs:
deploy:
runs-on: ubuntu-latest
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
python-version: ["3.8", "3.9", "3.10", "3.11"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.x"
python-version: ${{ matrix.python-version }}
- name: Add poetry to windows path
if: "startsWith(runner.os, 'windows')"
run: |
echo "C:\Users\runneradmin\.local\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Install and configure Poetry
uses: snok/install-poetry@v1.3.3
with:
version: ${{ env.POETRY_VERSION }}
virtualenvs-create: true
virtualenvs-in-project: true
- name: Display Python version
run: poetry run python -c "import sys; print(sys.version)"
- name: Build packages
run: poetry build
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
poetry run pip install --upgrade pip
poetry run pip install twine
- name: Build and publish
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
python setup.py sdist bdist_wheel
twine upload dist/*
poetry run twine upload dist/*.whl
- name: Build and publish (source)
if: ${{ startsWith(runner.os, 'windows') && matrix.python-version == '3.11' }}
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
run: |
poetry run twine upload dist/*.tar.gz

View File

@@ -20,25 +20,21 @@ jobs:
PYTHON_VERSION: ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
# Poetry cache depends on OS, Python version and Poetry version.
- name: Cache Poetry cache
uses: actions/cache@v3
with:
path: ~/.cache/pypoetry
key: poetry-cache-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ env.POETRY_VERSION }}
# virtualenv cache should depends on OS, Python version and `poetry.lock` (and optionally workflow files).
- name: Cache Packages
uses: actions/cache@v3
if: ${{ !startsWith(runner.os, 'windows') }}
with:
path: ~/.local
path: |
~/.local
.venv
key: poetry-${{ runner.os }}-${{ env.PYTHON_VERSION }}-${{ hashFiles('**/poetry.lock') }}
- name: Add poetry to windows path
@@ -53,12 +49,14 @@ jobs:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Display Python version
run: poetry run python -c "import sys; print(sys.version)"
- name: Install dependencies
run: |
poetry install
- name: Type checking
if: ${{ env.PYTHON_VERSION != '3.7' }}
run: |
poetry run mypy pyModeS
@@ -67,7 +65,6 @@ jobs:
poetry run pytest tests --cov --cov-report term-missing
- name: Upload coverage to Codecov
if: ${{ github.event_name != 'pull_request_target' && env.PYTHON_VERSION == '3.10' }}
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v3
with:
env_vars: PYTHON_VERSION

View File

@@ -1,9 +1,10 @@
import os
import shutil
import sys
from distutils.core import Distribution, Extension
from distutils.command import build_ext
from distutils.core import Distribution, Extension
# import pip
from Cython.Build import cythonize
@@ -11,16 +12,7 @@ def build() -> None:
compile_args = []
if sys.platform == "linux":
compile_args += [
"-march=native",
"-O3",
"-msse",
"-msse2",
"-mfma",
"-mfpmath=sse",
"-Wno-pointer-sign",
"-Wno-unused-variable",
]
compile_args += ["-Wno-pointer-sign", "-Wno-unused-variable"]
extensions = [
Extension(

1009
poetry.lock generated

File diff suppressed because it is too large Load Diff

3
pyModeS/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
decoder/flarm/decode.c
extra/demod2400/core.c
c_common.c

View File

@@ -82,7 +82,7 @@ def airborne_position(
if lon > 180:
lon = lon - 360
return round(lat, 5), round(lon, 5)
return lat, lon
def airborne_position_with_ref(
@@ -129,7 +129,7 @@ def airborne_position_with_ref(
lon = d_lon * (m + cprlon)
return round(lat, 5), round(lon, 5)
return lat, lon
def altitude(msg: str) -> None | int:

View File

@@ -19,7 +19,6 @@ def surface_position(
lat_ref: float,
lon_ref: float,
) -> None | tuple[float, float]:
"""Decode surface position from a pair of even and odd position message,
the lat/lon of receiver must be provided to yield the correct solution.
@@ -92,7 +91,7 @@ def surface_position(
imin = min(range(4), key=dls.__getitem__)
lon = lons[imin]
return round(lat, 5), round(lon, 5)
return lat, lon
def surface_position_with_ref(
@@ -139,12 +138,12 @@ def surface_position_with_ref(
lon = d_lon * (m + cprlon)
return round(lat, 5), round(lon, 5)
return lat, lon
def surface_velocity(
msg: str, source: bool = False
) -> tuple[None | float, float, int, str]:
) -> tuple[None | float, None | float, int, str]:
"""Decode surface velocity from a surface position message
Args:
@@ -173,7 +172,6 @@ def surface_velocity(
trk_status = int(mb[12])
if trk_status == 1:
trk = common.bin2int(mb[13:20]) * 360 / 128
trk = round(trk, 1)
else:
trk = None

View File

@@ -48,7 +48,6 @@ def airborne_velocity(
spd: None | float
if subtype in (1, 2):
v_ew = common.bin2int(mb[14:24])
v_ns = common.bin2int(mb[25:35])
@@ -77,7 +76,7 @@ def airborne_velocity(
trk = math.degrees(trk) # convert to degrees
trk = trk if trk >= 0 else trk + 360 # no negative val
trk_or_hdg = round(trk, 2)
trk_or_hdg = trk
spd_type = "GS"
dir_type = "TRUE_NORTH"
@@ -87,7 +86,6 @@ def airborne_velocity(
hdg = None
else:
hdg = common.bin2int(mb[14:24]) / 1024 * 360.0
hdg = round(hdg, 2)
trk_or_hdg = hdg

View File

@@ -72,7 +72,7 @@ def wind44(msg: str) -> Tuple[Optional[int], Optional[float]]:
speed = common.bin2int(d[5:14]) # knots
direction = common.bin2int(d[14:23]) * 180 / 256 # degree
return round(speed, 0), round(direction, 1)
return speed, direction
def temp44(msg: str) -> Tuple[float, float]:
@@ -96,10 +96,8 @@ def temp44(msg: str) -> Tuple[float, float]:
value = value - 1024
temp = value * 0.25 # celsius
temp = round(temp, 2)
temp_alternative = value * 0.125 # celsius
temp_alternative = round(temp_alternative, 3)
return temp, temp_alternative
@@ -140,7 +138,7 @@ def hum44(msg: str) -> Optional[float]:
hm = common.bin2int(d[50:56]) * 100 / 64 # %
return round(hm, 1)
return hm
def turb44(msg: str) -> Optional[int]:

View File

@@ -171,7 +171,6 @@ def temp45(msg: str) -> Optional[float]:
value = value - 512
temp = value * 0.25 # celsius
temp = round(temp, 1)
return temp

View File

@@ -81,7 +81,7 @@ def roll50(msg: str) -> Optional[float]:
value = value - 512
angle = value * 45 / 256 # degree
return round(angle, 1)
return angle
def trk50(msg: str) -> Optional[float]:
@@ -110,7 +110,7 @@ def trk50(msg: str) -> Optional[float]:
if trk < 0:
trk = 360 + trk
return round(trk, 3)
return trk
def gs50(msg: str) -> Optional[float]:
@@ -154,7 +154,7 @@ def rtrk50(msg: str) -> Optional[float]:
value = value - 512
angle = value * 8 / 256 # degree / sec
return round(angle, 3)
return angle
def tas50(msg: str) -> Optional[float]:

View File

@@ -86,7 +86,7 @@ def hdg53(msg: str) -> Optional[float]:
if hdg < 0:
hdg = 360 + hdg
return round(hdg, 3)
return hdg
def ias53(msg: str) -> Optional[float]:
@@ -122,7 +122,7 @@ def mach53(msg: str) -> Optional[float]:
return None
mach = common.bin2int(d[24:33]) * 0.008
return round(mach, 3)
return mach
def tas53(msg: str) -> Optional[float]:
@@ -140,7 +140,7 @@ def tas53(msg: str) -> Optional[float]:
return None
tas = common.bin2int(d[34:46]) * 0.5 # kts
return round(tas, 1)
return tas
def vr53(msg: str) -> Optional[int]:

View File

@@ -200,7 +200,6 @@ def selected_heading(msg: str) -> None | float:
else:
hdg_sign = int(mb[30])
hdg = (hdg_sign + 1) * common.bin2int(mb[31:39]) * (180 / 256)
hdg = round(hdg, 2)
return hdg

View File

@@ -130,17 +130,18 @@ def flarm(long timestamp, str msg, float refLat, float refLon, **kwargs):
return dict(
timestamp=timestamp,
icao24=icao24,
latitude=round(lat * 1e-7, 6),
longitude=round(lon * 1e-7, 6),
latitude=lat * 1e-7,
longitude=lon * 1e-7,
geoaltitude=altitude,
vertical_speed=raw_vs * mult_factor / 10,
groundspeed=round(speed),
track=round(heading4 - 4 * turningRate(heading4, heading8) / 4),
groundspeed=speed,
track=heading4 - 4 * turningRate(heading4, heading8) / 4,
type=AIRCRAFT_TYPES[aircraft_type],
sensorLatitude=refLat,
sensorLongitude=refLon,
isIcao24=magic==0x10,
noTrack=noTrack,
stealth=stealth,
gps=gps,
**kwargs
)

View File

@@ -1,14 +1,22 @@
from __future__ import annotations
import time
import traceback
import numpy as np
import pyModeS as pms
from typing import Any
import_msg = """
---------------------------------------------------------------------
Warning: pyrtlsdr not installed (required for using RTL-SDR devices)!
---------------------------------------------------------------------"""
try:
import rtlsdr # type: ignore
except:
print("------------------------------------------------------------------------")
print("! Warning: pyrtlsdr not installed (required for using RTL-SDR devices) !")
print("------------------------------------------------------------------------")
except ImportError:
print(import_msg)
sampling_rate = 2e6
smaples_per_microsec = 2
@@ -24,9 +32,9 @@ th_amp_diff = 0.8 # signal amplitude threshold difference between 0 and 1 bit
class RtlReader(object):
def __init__(self, **kwargs):
def __init__(self, **kwargs) -> None:
super(RtlReader, self).__init__()
self.signal_buffer = [] # amplitude of the sample only
self.signal_buffer: list[float] = [] # amplitude of the sample only
self.sdr = rtlsdr.RtlSdr()
self.sdr.sample_rate = sampling_rate
self.sdr.center_freq = modes_frequency
@@ -39,7 +47,7 @@ class RtlReader(object):
self.exception_queue = None
def _calc_noise(self):
def _calc_noise(self) -> float:
"""Calculate noise floor"""
window = smaples_per_microsec * 100
total_len = len(self.signal_buffer)
@@ -50,7 +58,7 @@ class RtlReader(object):
)
return min(means)
def _process_buffer(self):
def _process_buffer(self) -> list[list[Any]]:
"""process raw IQ data in the buffer"""
# update noise floor
@@ -70,17 +78,18 @@ class RtlReader(object):
i += 1
continue
if self._check_preamble(self.signal_buffer[i : i + pbits * 2]):
frame_start = i + pbits * 2
frame_end = i + pbits * 2 + (fbits + 1) * 2
frame_start = i + pbits * 2
if self._check_preamble(self.signal_buffer[i:frame_start]):
frame_length = (fbits + 1) * 2
frame_end = frame_start + frame_length
frame_pulses = self.signal_buffer[frame_start:frame_end]
threshold = max(frame_pulses) * 0.2
msgbin = []
msgbin: list[int] = []
for j in range(0, frame_length, 2):
p2 = frame_pulses[j : j + 2]
j_2 = j + 2
p2 = frame_pulses[j:j_2]
if len(p2) < 2:
break
@@ -117,7 +126,7 @@ class RtlReader(object):
return messages
def _check_preamble(self, pulses):
def _check_preamble(self, pulses) -> bool:
if len(pulses) != 16:
return False
@@ -127,7 +136,7 @@ class RtlReader(object):
return True
def _check_msg(self, msg):
def _check_msg(self, msg) -> bool:
df = pms.df(msg)
msglen = len(msg)
if df == 17 and msglen == 28:
@@ -137,8 +146,9 @@ class RtlReader(object):
return True
elif df in [4, 5, 11] and msglen == 14:
return True
return False
def _debug_msg(self, msg):
def _debug_msg(self, msg) -> None:
df = pms.df(msg)
msglen = len(msg)
if df == 17 and msglen == 28:
@@ -151,7 +161,7 @@ class RtlReader(object):
# print("[*]", msg)
pass
def _read_callback(self, data, rtlsdr_obj):
def _read_callback(self, data, rtlsdr_obj) -> None:
amp = np.absolute(data)
self.signal_buffer.extend(amp.tolist())
@@ -159,16 +169,18 @@ class RtlReader(object):
messages = self._process_buffer()
self.handle_messages(messages)
def handle_messages(self, messages):
def handle_messages(self, messages) -> None:
"""re-implement this method to handle the messages"""
for msg, t in messages:
# print("%15.9f %s" % (t, msg))
pass
def stop(self, *args, **kwargs):
def stop(self, *args, **kwargs) -> None:
self.sdr.close()
def run(self, raw_pipe_in=None, stop_flag=None, exception_queue=None):
def run(
self, raw_pipe_in=None, stop_flag=None, exception_queue=None
) -> None:
self.raw_pipe_in = raw_pipe_in
self.exception_queue = exception_queue
self.stop_flag = stop_flag

View File

@@ -231,7 +231,7 @@ def squawk(binstr: str) -> str:
binstr (String): 13 bits binary string
Returns:
int: altitude in ft
string: squawk code
"""
if len(binstr) != 13 or not set(binstr).issubset(set("01")):

View File

@@ -1,6 +1,6 @@
[tool.poetry]
name = "pyModeS"
version = "2.11"
version = "2.17"
description = "Python Mode-S and ADS-B Decoder"
authors = ["Junzi Sun <j.sun-1@tudelft.nl>"]
license = "GNU GPL v3"
@@ -22,7 +22,8 @@ include = [
"*.pxd",
"*.pyi",
"py.typed",
{ path = "src/pyModeS/**/*.so", format = "wheel" }
{ path = "pyModeS/**/*.so", format = "wheel" },
{ path = "pyModeS/**/*.pyd", format = "wheel" }
]
[tool.poetry.build]
@@ -39,7 +40,6 @@ pyzmq = "^24.0"
pyrtlsdr = {version = "^0.2.93", optional = true}
[tool.poetry.group.dev.dependencies]
Cython = "^0.29.32"
mypy = "^0.991"
flake8 = "^5.0.0"
black = "^22.12.0"
@@ -47,7 +47,6 @@ isort = "^5.11.4"
pytest = "^7.2.0"
pytest-cov = "^4.0.0"
codecov = "^2.1.12"
ipykernel = "^6.20.0"
[tool.poetry.extras]
rtlsdr = ["pyrtlsdr"]

View File

@@ -1,4 +1,5 @@
from pyModeS import adsb
from pytest import approx
# === TEST ADS-B package ===
@@ -22,7 +23,7 @@ def test_adsb_position():
1446332400,
1446332405,
)
assert pos == (49.81755, 6.08442)
assert pos == (approx(49.81755, 0.001), approx(6.08442, 0.001))
def test_adsb_position_swap_odd_even():
@@ -32,32 +33,32 @@ def test_adsb_position_swap_odd_even():
1446332405,
1446332400,
)
assert pos == (49.81755, 6.08442)
assert pos == (approx(49.81755, 0.001), approx(6.08442, 0.001))
def test_adsb_position_with_ref():
pos = adsb.position_with_ref("8D40058B58C901375147EFD09357", 49.0, 6.0)
assert pos == (49.82410, 6.06785)
assert pos == (approx(49.82410, 0.001), approx(6.06785, 0.001))
pos = adsb.position_with_ref("8FC8200A3AB8F5F893096B000000", -43.5, 172.5)
assert pos == (-43.48564, 172.53942)
assert pos == (approx(-43.48564, 0.001), approx(172.53942, 0.001))
def test_adsb_airborne_position_with_ref():
pos = adsb.airborne_position_with_ref(
"8D40058B58C901375147EFD09357", 49.0, 6.0
)
assert pos == (49.82410, 6.06785)
assert pos == (approx(49.82410, 0.001), approx(6.06785, 0.001))
pos = adsb.airborne_position_with_ref(
"8D40058B58C904A87F402D3B8C59", 49.0, 6.0
)
assert pos == (49.81755, 6.08442)
assert pos == (approx(49.81755, 0.001), approx(6.08442, 0.001))
def test_adsb_surface_position_with_ref():
pos = adsb.surface_position_with_ref(
"8FC8200A3AB8F5F893096B000000", -43.5, 172.5
)
assert pos == (-43.48564, 172.53942)
assert pos == (approx(-43.48564, 0.001), approx(172.53942, 0.001))
def test_adsb_surface_position():
@@ -69,7 +70,7 @@ def test_adsb_surface_position():
-43.496,
172.558,
)
assert pos == (-43.48564, 172.53942)
assert pos == (approx(-43.48564, 0.001), approx(172.53942, 0.001))
def test_adsb_alt():
@@ -80,9 +81,9 @@ def test_adsb_velocity():
vgs = adsb.velocity("8D485020994409940838175B284F")
vas = adsb.velocity("8DA05F219B06B6AF189400CBC33F")
vgs_surface = adsb.velocity("8FC8200A3AB8F5F893096B000000")
assert vgs == (159, 182.88, -832, "GS")
assert vas == (375, 243.98, -2304, "TAS")
assert vgs_surface == (19, 42.2, 0, "GS")
assert vgs == (159, approx(182.88, 0.1), -832, "GS")
assert vas == (375, approx(243.98, 0.1), -2304, "TAS")
assert vgs_surface == (19, approx(42.2, 0.1), 0, "GS")
assert adsb.altitude_diff("8D485020994409940838175B284F") == 550
@@ -96,7 +97,9 @@ def test_adsb_target_state_status():
sel_alt = adsb.selected_altitude("8DA05629EA21485CBF3F8CADAEEB")
assert sel_alt == (16992, "MCP/FCU")
assert adsb.baro_pressure_setting("8DA05629EA21485CBF3F8CADAEEB") == 1012.8
assert adsb.selected_heading("8DA05629EA21485CBF3F8CADAEEB") == 66.8
assert adsb.selected_heading("8DA05629EA21485CBF3F8CADAEEB") == approx(
66.8, 0.1
)
assert adsb.autopilot("8DA05629EA21485CBF3F8CADAEEB") is True
assert adsb.vnav_mode("8DA05629EA21485CBF3F8CADAEEB") is True
assert adsb.altitude_hold_mode("8DA05629EA21485CBF3F8CADAEEB") is False

View File

@@ -1,5 +1,5 @@
from pyModeS import bds, commb
import pytest
from pytest import approx
# from pyModeS import ehs, els # deprecated
@@ -23,32 +23,24 @@ def test_bds40_functions():
def test_bds50_functions():
assert bds.bds50.roll50("A000139381951536E024D4CCF6B5") == 2.1
assert bds.bds50.roll50("A0001691FFD263377FFCE02B2BF9") == -0.4
assert bds.bds50.trk50("A000139381951536E024D4CCF6B5") == 114.258
assert bds.bds50.gs50("A000139381951536E024D4CCF6B5") == 438
assert bds.bds50.rtrk50("A000139381951536E024D4CCF6B5") == 0.125
assert bds.bds50.tas50("A000139381951536E024D4CCF6B5") == 424
msg1 = "A000139381951536E024D4CCF6B5"
msg2 = "A0001691FFD263377FFCE02B2BF9"
assert commb.roll50("A000139381951536E024D4CCF6B5") == 2.1
assert commb.roll50("A0001691FFD263377FFCE02B2BF9") == -0.4 # signed value
assert commb.trk50("A000139381951536E024D4CCF6B5") == 114.258
assert commb.gs50("A000139381951536E024D4CCF6B5") == 438
assert commb.rtrk50("A000139381951536E024D4CCF6B5") == 0.125
assert commb.tas50("A000139381951536E024D4CCF6B5") == 424
for module in [bds.bds50, commb]:
assert module.roll50(msg1) == approx(2.1, 0.01)
assert module.roll50(msg2) == approx(-0.35, 0.01) # signed value
assert module.trk50(msg1) == approx(114.258, 0.1)
assert module.gs50(msg1) == 438
assert module.rtrk50(msg1) == 0.125
assert module.tas50(msg1) == 424
def test_bds60_functions():
msg = "A00004128F39F91A7E27C46ADC21"
assert bds.bds60.hdg60(msg) == pytest.approx(42.71484)
assert bds.bds60.ias60(msg) == 252
assert bds.bds60.mach60(msg) == 0.42
assert bds.bds60.vr60baro(msg) == -1920
assert bds.bds60.vr60ins(msg) == -1920
assert commb.hdg60(msg) == pytest.approx(42.71484)
assert commb.ias60(msg) == 252
assert commb.mach60(msg) == 0.42
assert commb.vr60baro(msg) == -1920
assert commb.vr60ins(msg) == -1920
for module in [bds.bds60, commb]:
assert bds.bds60.hdg60(msg) == approx(42.71484)
assert bds.bds60.ias60(msg) == 252
assert bds.bds60.mach60(msg) == 0.42
assert bds.bds60.vr60baro(msg) == -1920
assert bds.bds60.vr60ins(msg) == -1920