Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add vectorized version of df and typecode functions. #90

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions pyModeS/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import os
import warnings

try:
from . import c_common as common
from .c_common import *
except:
from . import py_common as common
from .py_common import *

from .common import *
from .decoder import tell
from .decoder import adsb
from .decoder import commb
Expand Down
48 changes: 48 additions & 0 deletions pyModeS/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import typing as tp
from functools import singledispatch

from .vec import common as common_vec
from .vec.types import InputData
try:
from . import c_common as common_str
except:
from . import py_common as common_str

@singledispatch
def df(msg: tp.Any) -> int:
raise NotImplementedError('Only string and NumPy arrays supported')

@df.register
def _df_str(msg: str) -> int:
return common_str.df(msg)

@df.register
def _df_vec(msg: InputData) -> int:
return common_vec.df(msg)

@singledispatch
def typecode(msg: tp.Any) -> int:
raise NotImplementedError('Only string and NumPy arrays supported')

@typecode.register
def _tc_str(msg: str) -> int:
return common_str.typecode(msg)

@typecode.register
def _tc_vec(msg: InputData) -> int:
return common_vec.typecode(msg)

icao = common_str.icao
altitude = common_str.altitude
altcode = common_str.altcode
allzeros = common_str.allzeros
data = common_str.data
wrongstatus = common_str.wrongstatus
idcode = common_str.idcode
floor = common_str.floor
cprNL = common_str.cprNL
crc = common_str.crc
squawk = common_str.squawk
hex2bin = common_str.hex2bin
hex2bin = common_str.hex2bin
bin2int = common_str.bin2int
Empty file added pyModeS/vec/__init__.py
Empty file.
34 changes: 34 additions & 0 deletions pyModeS/vec/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import numpy as np
import typing as tp

from .util import create_array
from .types import InputData, DownlinkFormat, TypeCode


def df(data: InputData) -> DownlinkFormat:
"""
Parse downlink format address from ADS-B messages.

:param data: ADS-B messages.

..seealso:: `Message structure <https://mode-s.org/decode/content/ads-b/1-basics.html>`_
"""
result = (data[:, 0] & 0xf8) >> 3
result[result > 24] = 24
return result

def typecode(data: InputData, df_data: tp.Optional[DownlinkFormat]=None) -> TypeCode:
"""
Parse type code information from ADS-B messages.

:param data: ADS-B messages.
:param df_data: Optional downlink format information for each ADS-B
message.

..seealso:: `ADS-B message types <https://mode-s.org/decode/content/ads-b/1-basics.html>`_
"""
result = np.zeros(len(data), dtype=np.uint8)
df_v = df(data) if df_data is None else df_data
idx = (df_v == 17) | (df_v == 18)
result[idx] = data[idx, 4] >> 3
return create_array(result, idx)
15 changes: 15 additions & 0 deletions pyModeS/vec/ctor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import numpy as np
import typing as tp

from .types import InputData

def array(data: tp.Sequence[bytes]) -> InputData:
"""
Create Numpy array, which is input for parsing functions.

:param data: Collection of ADS-B messages.
"""
vec = np.array(data)
result = vec.view(dtype=np.uint8)
return result.reshape(-1, 14)

9 changes: 9 additions & 0 deletions pyModeS/vec/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import numpy as np

# define aliases for basic types to support static type analysis; these for
# convenience
# NOTE: more specific types can be defined when numpy 1.20 is released,
# i.e. use of np.ndarray[dtype, shape] shall be possible
InputData = np.ndarray
DownlinkFormat = np.ndarray
TypeCode = np.ndarray
16 changes: 16 additions & 0 deletions pyModeS/vec/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import numpy as np

def create_array(data, idx):
"""
Create NumPy masked array.

Note, that this function differes from NumPy constructor semantics. The
index indicates the valid values (not the invalid as in the default
masked array in NumPy).

:param data: Input data.
:param idx: Index of valid values.
"""
return np.ma.array(data, mask=~idx)

# vim: sw=4:et:ai
Empty file added tests/vec/__init__.py
Empty file.
26 changes: 26 additions & 0 deletions tests/vec/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import numpy as np
from binascii import unhexlify

from pyModeS.vec.ctor import array
from pyModeS.vec.common import df

import pytest

@pytest.fixture
def message():
data = [
'904ca3a33219741c85465ae1b4c3', # df = 18
'a8281d3030000000000000850d4a', # df = 21
'8d406b902015a678d4d220000000', # example from https://mode-s.org/decode/content/ads-b/8-error-control.html
'904ca3da121010603d04f5df3ecf', # df = 18, tc = 2
'977ba7ca0daa3e1915d83237c86e', # df = 18, tc = 1
'8d4ca2d4234994b5452820e5b7ab', # df = 17, tc = 4
]
return array(np.array([unhexlify(v) for v in data]))

@pytest.fixture
def df_message(message):
return df(message)

# vim: sw=4:et:ai

20 changes: 20 additions & 0 deletions tests/vec/test_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import numpy as np

from pyModeS.common import df, typecode

from .data import message

def test_df(message):
"""
Test parsing of ADS-B downlink format.
"""
result = df(message)
assert np.array_equal([18, 21, 17, 18, 18, 17], result)

def test_typecode(message):
"""
Test ADS-B type code parsing.
"""
result = typecode(message)
assert np.array_equal([6, 0, 4, 2, 1, 4], result)
assert np.array_equal([6, 4, 2, 1, 4], result.compressed())