From 3bb8c361e99d9d8e025f5f26ef804452b261d0fe Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jan 2021 01:21:54 +0100 Subject: [PATCH 1/3] bug fix: altiude and TAS --- pyModeS/decoder/bds/bds05.py | 31 +++++++++++---------- pyModeS/decoder/bds/bds09.py | 52 +++++++++++++++++++++--------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/pyModeS/decoder/bds/bds05.py b/pyModeS/decoder/bds/bds05.py index 120c31c..2f620b3 100644 --- a/pyModeS/decoder/bds/bds05.py +++ b/pyModeS/decoder/bds/bds05.py @@ -34,13 +34,13 @@ def airborne_position(msg0, msg1, t0, t1): raise RuntimeError("Both even and odd CPR frames are required.") # 131072 is 2^17, since CPR lat and lon are 17 bits each. - cprlat_even = common.bin2int(mb0[22:39]) / 131072.0 - cprlon_even = common.bin2int(mb0[39:56]) / 131072.0 - cprlat_odd = common.bin2int(mb1[22:39]) / 131072.0 - cprlon_odd = common.bin2int(mb1[39:56]) / 131072.0 + cprlat_even = common.bin2int(mb0[22:39]) / 131072 + cprlon_even = common.bin2int(mb0[39:56]) / 131072 + cprlat_odd = common.bin2int(mb1[22:39]) / 131072 + cprlon_odd = common.bin2int(mb1[39:56]) / 131072 - air_d_lat_even = 360.0 / 60 - air_d_lat_odd = 360.0 / 59 + air_d_lat_even = 360 / 60 + air_d_lat_odd = 360 / 59 # compute latitude index 'j' j = common.floor(59 * cprlat_even - 60 * cprlat_odd + 0.5) @@ -64,13 +64,13 @@ def airborne_position(msg0, msg1, t0, t1): nl = common.cprNL(lat) ni = max(common.cprNL(lat) - 0, 1) m = common.floor(cprlon_even * (nl - 1) - cprlon_odd * nl + 0.5) - lon = (360.0 / ni) * (m % ni + cprlon_even) + lon = (360 / ni) * (m % ni + cprlon_even) else: lat = lat_odd nl = common.cprNL(lat) ni = max(common.cprNL(lat) - 1, 1) m = common.floor(cprlon_even * (nl - 1) - cprlon_odd * nl + 0.5) - lon = (360.0 / ni) * (m % ni + cprlon_odd) + lon = (360 / ni) * (m % ni + cprlon_odd) if lon > 180: lon = lon - 360 @@ -95,11 +95,11 @@ def airborne_position_with_ref(msg, lat_ref, lon_ref): mb = common.hex2bin(msg)[32:] - cprlat = common.bin2int(mb[22:39]) / 131072.0 - cprlon = common.bin2int(mb[39:56]) / 131072.0 + cprlat = common.bin2int(mb[22:39]) / 131072 + cprlon = common.bin2int(mb[39:56]) / 131072 i = int(mb[21]) - d_lat = 360.0 / 59 if i else 360.0 / 60 + d_lat = 360 / 59 if i else 360 / 60 j = common.floor(lat_ref / d_lat) + common.floor( 0.5 + ((lat_ref % d_lat) / d_lat) - cprlat @@ -110,9 +110,9 @@ def airborne_position_with_ref(msg, lat_ref, lon_ref): ni = common.cprNL(lat) - i if ni > 0: - d_lon = 360.0 / ni + d_lon = 360 / ni else: - d_lon = 360.0 + d_lon = 360 m = common.floor(lon_ref / d_lon) + common.floor( 0.5 + ((lon_ref % d_lon) / d_lon) - cprlon @@ -143,9 +143,8 @@ def altitude(msg): if tc < 19: altcode = altbin[0:6] + "0" + altbin[6:] + alt = common.altitude(altcode) else: - altcode = altbin[0:6] + "0" + altbin[6:] - - alt = common.altitude(altcode) + alt = common.bin2int(altbin) * 3.28084 return alt diff --git a/pyModeS/decoder/bds/bds09.py b/pyModeS/decoder/bds/bds09.py index 43acf00..c7302e4 100644 --- a/pyModeS/decoder/bds/bds09.py +++ b/pyModeS/decoder/bds/bds09.py @@ -24,7 +24,7 @@ def airborne_velocity(msg, source=False): - Angle (degree), either ground track or heading - Vertical rate (ft/min) - Speed type ('GS' for ground speed, 'AS' for airspeed) - - [Optional] Direction source ('TRUE_NORTH' or 'MAGENTIC_NORTH') + - [Optional] Direction source ('TRUE_NORTH' or 'MAGNETIC_NORTH') - [Optional] Vertical rate source ('BARO' or 'GNSS') """ @@ -35,29 +35,35 @@ def airborne_velocity(msg, source=False): subtype = common.bin2int(mb[5:8]) - if common.bin2int(mb[14:24]) == 0 or common.bin2int(mb[25:35]) == 0: - return None - if subtype in (1, 2): - v_ew_sign = -1 if mb[13] == "1" else 1 - v_ew = common.bin2int(mb[14:24]) - 1 # east-west velocity - if subtype == 2: # Supersonic - v_ew *= 4 - v_ns_sign = -1 if mb[24] == "1" else 1 - v_ns = common.bin2int(mb[25:35]) - 1 # north-south velocity - if subtype == 2: # Supersonic - v_ns *= 4 + v_ew = common.bin2int(mb[14:24]) + v_ns = common.bin2int(mb[25:35]) - v_we = v_ew_sign * v_ew - v_sn = v_ns_sign * v_ns + if v_ew == 0 or v_ns == 0: + spd = None + trk_or_hdg = None + vs = None + else: + v_ew_sign = -1 if mb[13] == "1" else 1 + v_ew = v_ew - 1 # east-west velocity + if subtype == 2: # Supersonic + v_ew *= 4 - spd = math.sqrt(v_sn * v_sn + v_we * v_we) # unit in kts - spd = int(spd) + v_ns_sign = -1 if mb[24] == "1" else 1 + v_ns = v_ns - 1 # north-south velocity + if subtype == 2: # Supersonic + v_ns *= 4 - trk = math.atan2(v_we, v_sn) - trk = math.degrees(trk) # convert to degrees - trk = trk if trk >= 0 else trk + 360 # no negative val + v_we = v_ew_sign * v_ew + v_sn = v_ns_sign * v_ns + + spd = math.sqrt(v_sn * v_sn + v_we * v_we) # unit in kts + spd = int(spd) + + trk = math.atan2(v_we, v_sn) + trk = math.degrees(trk) # convert to degrees + trk = trk if trk >= 0 else trk + 360 # no negative val spd_type = "GS" trk_or_hdg = round(trk, 2) @@ -67,13 +73,15 @@ def airborne_velocity(msg, source=False): if mb[13] == "0": hdg = None else: - hdg = common.bin2int(mb[14:24]) / 1024.0 * 360.0 + hdg = common.bin2int(mb[14:24]) / 1024 * 360.0 hdg = round(hdg, 2) trk_or_hdg = hdg spd = common.bin2int(mb[25:35]) + spd = None if spd == 0 else spd - 1 + if subtype == 4: # Supersonic spd *= 4 @@ -82,7 +90,7 @@ def airborne_velocity(msg, source=False): else: spd_type = "TAS" - dir_type = "MAGENTIC_NORTH" + dir_type = "MAGNETIC_NORTH" vr_source = "GNSS" if mb[35] == "0" else "BARO" vr_sign = -1 if mb[36] == "1" else 1 @@ -96,7 +104,7 @@ def airborne_velocity(msg, source=False): def altitude_diff(msg): - """Decode the differece between GNSS and barometric altitude. + """Decode the difference between GNSS and barometric altitude. Args: msg (str): 28 hexdigits string, TC=19 From a4ce3bfaf1206a459b66a8f6ed01953839a9940f Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jan 2021 01:22:19 +0100 Subject: [PATCH 2/3] number format --- pyModeS/c_common.pyx | 2 +- pyModeS/decoder/bds/bds06.py | 34 +++++++++++++++++----------------- pyModeS/decoder/bds/bds44.py | 4 ++-- pyModeS/decoder/bds/bds50.py | 6 +++--- pyModeS/decoder/bds/bds53.py | 2 +- pyModeS/decoder/bds/bds60.py | 2 +- pyModeS/extra/aero.py | 18 +++++++++--------- pyModeS/py_common.py | 2 +- tests/test_allcall.py | 2 +- 9 files changed, 36 insertions(+), 36 deletions(-) diff --git a/pyModeS/c_common.pyx b/pyModeS/c_common.pyx index 18c1768..ab9f989 100644 --- a/pyModeS/c_common.pyx +++ b/pyModeS/c_common.pyx @@ -228,7 +228,7 @@ cpdef int cprNL(double lat): cdef int nz = 15 cdef double a = 1 - cos(pi / (2 * nz)) - cdef double b = cos(pi / 180.0 * fabs(lat)) ** 2 + cdef double b = cos(pi / 180 * fabs(lat)) ** 2 cdef double nl = 2 * pi / (acos(1 - a / b)) NL = floor(nl) return NL diff --git a/pyModeS/decoder/bds/bds06.py b/pyModeS/decoder/bds/bds06.py index bd7a8f8..46cee1a 100644 --- a/pyModeS/decoder/bds/bds06.py +++ b/pyModeS/decoder/bds/bds06.py @@ -27,13 +27,13 @@ def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref): msgbin1 = common.hex2bin(msg1) # 131072 is 2^17, since CPR lat and lon are 17 bits each. - cprlat_even = common.bin2int(msgbin0[54:71]) / 131072.0 - cprlon_even = common.bin2int(msgbin0[71:88]) / 131072.0 - cprlat_odd = common.bin2int(msgbin1[54:71]) / 131072.0 - cprlon_odd = common.bin2int(msgbin1[71:88]) / 131072.0 + cprlat_even = common.bin2int(msgbin0[54:71]) / 131072 + cprlon_even = common.bin2int(msgbin0[71:88]) / 131072 + cprlat_odd = common.bin2int(msgbin1[54:71]) / 131072 + cprlon_odd = common.bin2int(msgbin1[71:88]) / 131072 - air_d_lat_even = 90.0 / 60 - air_d_lat_odd = 90.0 / 59 + air_d_lat_even = 90 / 60 + air_d_lat_odd = 90 / 59 # compute latitude index 'j' j = common.floor(59 * cprlat_even - 60 * cprlat_odd + 0.5) @@ -43,8 +43,8 @@ def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref): lat_odd_n = float(air_d_lat_odd * (j % 59 + cprlat_odd)) # solution for north hemisphere - lat_even_s = lat_even_n - 90.0 - lat_odd_s = lat_odd_n - 90.0 + lat_even_s = lat_even_n - 90 + lat_odd_s = lat_odd_n - 90 # chose which solution corrispondes to receiver location lat_even = lat_even_n if lat_ref > 0 else lat_even_s @@ -60,16 +60,16 @@ def surface_position(msg0, msg1, t0, t1, lat_ref, lon_ref): nl = common.cprNL(lat_even) ni = max(common.cprNL(lat_even) - 0, 1) m = common.floor(cprlon_even * (nl - 1) - cprlon_odd * nl + 0.5) - lon = (90.0 / ni) * (m % ni + cprlon_even) + lon = (90 / ni) * (m % ni + cprlon_even) else: lat = lat_odd nl = common.cprNL(lat_odd) ni = max(common.cprNL(lat_odd) - 1, 1) m = common.floor(cprlon_even * (nl - 1) - cprlon_odd * nl + 0.5) - lon = (90.0 / ni) * (m % ni + cprlon_odd) + lon = (90 / ni) * (m % ni + cprlon_odd) # four possible longitude solutions - lons = [lon, lon + 90.0, lon + 180.0, lon + 270.0] + lons = [lon, lon + 90, lon + 180, lon + 270] # make sure lons are between -180 and 180 lons = [(l + 180) % 360 - 180 for l in lons] @@ -99,11 +99,11 @@ def surface_position_with_ref(msg, lat_ref, lon_ref): mb = common.hex2bin(msg)[32:] - cprlat = common.bin2int(mb[22:39]) / 131072.0 - cprlon = common.bin2int(mb[39:56]) / 131072.0 + cprlat = common.bin2int(mb[22:39]) / 131072 + cprlon = common.bin2int(mb[39:56]) / 131072 i = int(mb[21]) - d_lat = 90.0 / 59 if i else 90.0 / 60 + d_lat = 90 / 59 if i else 90 / 60 j = common.floor(lat_ref / d_lat) + common.floor( 0.5 + ((lat_ref % d_lat) / d_lat) - cprlat @@ -114,9 +114,9 @@ def surface_position_with_ref(msg, lat_ref, lon_ref): ni = common.cprNL(lat) - i if ni > 0: - d_lon = 90.0 / ni + d_lon = 90 / ni else: - d_lon = 90.0 + d_lon = 90 m = common.floor(lon_ref / d_lon) + common.floor( 0.5 + ((lon_ref % d_lon) / d_lon) - cprlon @@ -153,7 +153,7 @@ def surface_velocity(msg, source=False): # ground track trk_status = int(mb[12]) if trk_status == 1: - trk = common.bin2int(mb[13:20]) * 360.0 / 128.0 + trk = common.bin2int(mb[13:20]) * 360 / 128 trk = round(trk, 1) else: trk = None diff --git a/pyModeS/decoder/bds/bds44.py b/pyModeS/decoder/bds/bds44.py index a2198a3..6bc755a 100644 --- a/pyModeS/decoder/bds/bds44.py +++ b/pyModeS/decoder/bds/bds44.py @@ -68,7 +68,7 @@ def wind44(msg): return None, None speed = common.bin2int(d[5:14]) # knots - direction = common.bin2int(d[14:23]) * 180.0 / 256.0 # degree + direction = common.bin2int(d[14:23]) * 180 / 256 # degree return round(speed, 0), round(direction, 1) @@ -136,7 +136,7 @@ def hum44(msg): if d[49] == "0": return None - hm = common.bin2int(d[50:56]) * 100.0 / 64 # % + hm = common.bin2int(d[50:56]) * 100 / 64 # % return round(hm, 1) diff --git a/pyModeS/decoder/bds/bds50.py b/pyModeS/decoder/bds/bds50.py index e20bd38..62dd186 100644 --- a/pyModeS/decoder/bds/bds50.py +++ b/pyModeS/decoder/bds/bds50.py @@ -78,7 +78,7 @@ def roll50(msg): if sign: value = value - 512 - angle = value * 45.0 / 256.0 # degree + angle = value * 45 / 256 # degree return round(angle, 1) @@ -102,7 +102,7 @@ def trk50(msg): if sign: value = value - 1024 - trk = value * 90.0 / 512.0 + trk = value * 90 / 512.0 # convert from [-180, 180] to [0, 360] if trk < 0: @@ -151,7 +151,7 @@ def rtrk50(msg): if sign: value = value - 512 - angle = value * 8.0 / 256.0 # degree / sec + angle = value * 8 / 256 # degree / sec return round(angle, 3) diff --git a/pyModeS/decoder/bds/bds53.py b/pyModeS/decoder/bds/bds53.py index 023f7ef..d7c47b9 100644 --- a/pyModeS/decoder/bds/bds53.py +++ b/pyModeS/decoder/bds/bds53.py @@ -78,7 +78,7 @@ def hdg53(msg): if sign: value = value - 1024 - hdg = value * 90.0 / 512.0 # degree + hdg = value * 90 / 512 # degree # convert from [-180, 180] to [0, 360] if hdg < 0: diff --git a/pyModeS/decoder/bds/bds60.py b/pyModeS/decoder/bds/bds60.py index 74255db..5ef19c5 100644 --- a/pyModeS/decoder/bds/bds60.py +++ b/pyModeS/decoder/bds/bds60.py @@ -86,7 +86,7 @@ def hdg60(msg): if sign: value = value - 1024 - hdg = value * 90 / 512.0 # degree + hdg = value * 90 / 512 # degree # convert from [-180, 180] to [0, 360] if hdg < 0: diff --git a/pyModeS/extra/aero.py b/pyModeS/extra/aero.py index 8d6a32c..a27e4cb 100644 --- a/pyModeS/extra/aero.py +++ b/pyModeS/extra/aero.py @@ -35,18 +35,18 @@ ft = 0.3048 # ft -> m fpm = 0.00508 # ft/min -> m/s inch = 0.0254 # inch -> m sqft = 0.09290304 # 1 square foot -nm = 1852.0 # nautical mile -> m +nm = 1852 # nautical mile -> m lbs = 0.453592 # pound -> kg g0 = 9.80665 # m/s2, Sea level gravity constant R = 287.05287 # m2/(s2 x K), gas constant, sea level ISA -p0 = 101325.0 # Pa, air pressure, sea level ISA +p0 = 101325 # Pa, air pressure, sea level ISA rho0 = 1.225 # kg/m3, air density, sea level ISA T0 = 288.15 # K, temperature, sea level ISA gamma = 1.40 # cp/cv for air gamma1 = 0.2 # (gamma-1)/2 for air gamma2 = 3.5 # gamma/(gamma-1) for air beta = -0.0065 # [K/m] ISA temp gradient below tropopause -r_earth = 6371000.0 # m, average earth radius +r_earth = 6371000 # m, average earth radius a0 = 340.293988 # m/s, sea level speed of sound ISA, sqrt(gamma*R*T0) @@ -94,8 +94,8 @@ def distance(lat1, lon1, lat2, lon2, H=0): """ # phi = 90 - latitude - phi1 = np.radians(90.0 - lat1) - phi2 = np.radians(90.0 - lat2) + phi1 = np.radians(90 - lat1) + phi2 = np.radians(90 - lat2) # theta = longitude theta1 = np.radians(lon1) @@ -158,16 +158,16 @@ def tas2eas(Vtas, H): def cas2tas(Vcas, H): """Calibrated Airspeed to True Airspeed""" p, rho, T = atmos(H) - qdyn = p0 * ((1.0 + rho0 * Vcas * Vcas / (7.0 * p0)) ** 3.5 - 1.0) - Vtas = np.sqrt(7.0 * p / rho * ((1.0 + qdyn / p) ** (2.0 / 7.0) - 1.0)) + qdyn = p0 * ((1 + rho0 * Vcas * Vcas / (7 * p0)) ** 3.5 - 1.0) + Vtas = np.sqrt(7 * p / rho * ((1 + qdyn / p) ** (2 / 7.0) - 1.0)) return Vtas def tas2cas(Vtas, H): """True Airspeed to Calibrated Airspeed""" p, rho, T = atmos(H) - qdyn = p * ((1.0 + rho * Vtas * Vtas / (7.0 * p)) ** 3.5 - 1.0) - Vcas = np.sqrt(7.0 * p0 / rho0 * ((qdyn / p0 + 1.0) ** (2.0 / 7.0) - 1.0)) + qdyn = p * ((1 + rho * Vtas * Vtas / (7 * p)) ** 3.5 - 1.0) + Vcas = np.sqrt(7 * p0 / rho0 * ((qdyn / p0 + 1.0) ** (2 / 7.0) - 1.0)) return Vcas diff --git a/pyModeS/py_common.py b/pyModeS/py_common.py index 4310ac3..0b299ee 100644 --- a/pyModeS/py_common.py +++ b/pyModeS/py_common.py @@ -199,7 +199,7 @@ def cprNL(lat: float) -> int: nz = 15 a = 1 - np.cos(np.pi / (2 * nz)) - b = np.cos(np.pi / 180.0 * abs(lat)) ** 2 + b = np.cos(np.pi / 180 * abs(lat)) ** 2 nl = 2 * np.pi / (np.arccos(1 - a / b)) NL = floor(nl) return NL diff --git a/tests/test_allcall.py b/tests/test_allcall.py index e727d4b..b10a839 100644 --- a/tests/test_allcall.py +++ b/tests/test_allcall.py @@ -6,7 +6,7 @@ def test_icao(): def test_interrogator(): - assert allcall.interrogator("5D484FDEA248F5") == 22 + assert allcall.interrogator("5D484FDEA248F5") == "SI6" def test_capability(): From b8f8f4dbc09d4e474fa3b09ec88b4f819bf2161e Mon Sep 17 00:00:00 2001 From: Junzi Sun Date: Sat, 23 Jan 2021 01:35:04 +0100 Subject: [PATCH 3/3] add github action for publishing on pypi --- .github/workflows/pypi-publish.yml | 29 +++++++++++++++++++++++++++++ setup.py | 2 +- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pypi-publish.yml diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 0000000..eb6956a --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,29 @@ +# 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 + +on: + release: + types: [created] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install setuptools wheel 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/* diff --git a/setup.py b/setup.py index 0e0d7a3..dd688bf 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ with open(path.join(here, "README.rst"), encoding="utf-8") as f: details = dict( name="pyModeS", - version="2.8", + version="2.9", description="Python Mode-S and ADS-B Decoder", long_description=long_description, url="https://github.com/junzis/pyModeS",