diff --git a/README.rst b/README.rst index 2db2e20..194b0f6 100644 --- a/README.rst +++ b/README.rst @@ -60,7 +60,7 @@ Core functions for ADS-B decoding: pms.adsb.icao(msg) pms.adsb.callsign(msg) - pms.adsb.position(msg_odd, msg_even, t_odd, t_even) + pms.adsb.position(msg_even, msg_odd, t_even, t_odd) pms.adsb.altitude(msg) pms.adsb.velocity(msg) pms.adsb.speed_heading(msg) diff --git a/pyModeS/adsb.py b/pyModeS/adsb.py index 34b6ede..90a1d0b 100644 --- a/pyModeS/adsb.py +++ b/pyModeS/adsb.py @@ -222,11 +222,99 @@ def airborne_position(msg0, msg1, t0, t1): return round(lat, 5), round(lon, 5) +def position_with_ref(msg, lat_ref, lon_ref): + if 5 <= typecode(msg) <= 8: + return airborne_position_with_ref(msg, lat_ref, lon_ref) + + elif 9 <= typecode(msg) <= 18: + return surface_position_with_ref(msg, lat_ref, lon_ref) + + else: + raise RuntimeError("incorrect or inconsistant message types") + + +def airborne_position_with_ref(msg, lat_ref, lon_ref): + """Decode airborn position with one message, + knowing previous reference location + Args: + msg (string): even message (28 bytes hexadecimal string) + lat_ref: previous known latitude + lon_ref: previous known longitude + Returns: + (float, float): (latitude, longitude) of the aircraft + """ + + i = oe_flag(msg) + d_lat = 360.0/59 if i else 360.0/60 + + msgbin = util.hex2bin(msg) + cprlat = util.bin2int(msgbin[54:71]) / 131072.0 + cprlon = util.bin2int(msgbin[71:88]) / 131072.0 + + j = util.floor(lat_ref / d_lat) \ + + util.floor(0.5 + ((lat_ref % d_lat) / d_lat) - cprlat) + + lat = d_lat * (j + cprlat) + + ni = _cprNL(lat) - i + + if ni > 0: + d_lon = 360.0 / ni + else: + d_lon = 360.0 + + m = util.floor(lon_ref / d_lon) \ + + util.floor(0.5 + ((lon_ref % d_lon) / d_lon) - cprlon) + + lon = d_lon * (m + cprlon) + + return round(lat, 5), round(lon, 5) + + def surface_position(msg0, msg1, t0, t1): - # TODO: implement surface positon decoding + # TODO: implement surface positon raise RuntimeError('suface position decoding to be implemented soon...') +def surface_position_with_ref(msg, lat_ref, lon_ref): + """Decode surface position with one message, + knowing reference nearby location, such as previously calculated location, + ground station, or airport location, etc. + Args: + msg (string): even message (28 bytes hexadecimal string) + lat_ref: previous known latitude + lon_ref: previous known longitude + Returns: + (float, float): (latitude, longitude) of the aircraft + """ + + i = oe_flag(msg) + d_lat = 90.0/59 if i else 90.0/60 + + msgbin = util.hex2bin(msg) + cprlat = util.bin2int(msgbin[54:71]) / 131072.0 + cprlon = util.bin2int(msgbin[71:88]) / 131072.0 + + j = util.floor(lat_ref / d_lat) \ + + util.floor(0.5 + ((lat_ref % d_lat) / d_lat) - cprlat) + + lat = d_lat * (j + cprlat) + + ni = _cprNL(lat) - i + + if ni > 0: + d_lon = 360.0 / ni + else: + d_lon = 360.0 + + m = util.floor(lon_ref / d_lon) \ + + util.floor(0.5 + ((lon_ref % d_lon) / d_lon) - cprlon) + + lon = d_lon * (m + cprlon) + + return round(lat, 5), round(lon, 5) + + def _cprNL(lat): if lat == 0: return 59 @@ -356,8 +444,8 @@ def velocity(msg): tag = 'AS' vr_sign = util.bin2int(msgbin[68]) - vr = util.bin2int(msgbin[68:77]) # vertical rate - rocd = -1*vr if vr_sign else vr # rate of climb/descend + vr = util.bin2int(msgbin[69:78]) # vertical rate + rocd = -1*vr if vr_sign else vr # rate of climb/descend return int(spd), round(hdg, 1), int(rocd), tag diff --git a/tests/run.py b/tests/run.py index 60fa629..f8db80c 100644 --- a/tests/run.py +++ b/tests/run.py @@ -39,13 +39,28 @@ def test_adsb_callsign(): assert adsb.callsign("8D406B902015A678D4D220AA4BDA") == "EZY85MH_" -def test_adsb_position(): - pos = adsb.position("8D40058B58C901375147EFD09357", - "8D40058B58C904A87F402D3B8C59", - 1446332400, 1446332405) +def test_adsb_airborne_position(): + pos = adsb.airborne_position("8D40058B58C901375147EFD09357", + "8D40058B58C904A87F402D3B8C59", + 1446332400, 1446332405) assert pos == (49.81755, 6.08442) +def test_adsb_airborne_position_with_ref(): + pos = adsb.airborne_position_with_ref("8D40058B58C901375147EFD09357", + 49.0, 6.0) + assert pos == (49.82410, 6.06785) + pos = adsb.airborne_position_with_ref("8D40058B58C904A87F402D3B8C59", + 49.0, 6.0) + assert pos == (49.81755, 6.08442) + + +def test_adsb_surface_position_with_ref(): + pos = adsb.surface_position_with_ref("8FC8200A3AB8F5F893096B22B4A8", + -43.5, 172.5) + assert pos == (-43.48564, 175.87195) + + def test_adsb_alt(): assert adsb.altitude("8D40058B58C901375147EFD09357") == 39000 @@ -53,8 +68,8 @@ def test_adsb_alt(): def test_adsb_velocity(): vgs = adsb.velocity("8D485020994409940838175B284F") vas = adsb.velocity("8DA05F219B06B6AF189400CBC33F") - assert vgs == (159, 182.9, -263, 'GS') - assert vas == (376, 244.0, -274, 'AS') + assert vgs == (159, 182.9, -14, 'GS') + assert vas == (376, 244.0, -37, 'AS') def test_nic(): @@ -183,5 +198,9 @@ def ehs_decode_all(n=None): print ts, m, icao, vBDS if __name__ == '__main__': - adsb_decode_all(100) - ehs_decode_all(100) + # adsb_decode_all(100) + # ehs_decode_all(100) + + + adsb.airborne_position_with_ref("8D40058B58C901375147EFD09357", + 49.0, 6.0) \ No newline at end of file