[python] get air traffic from opensky-network and display in GCS (#2259)

This commit is contained in:
Gautier Hattenberger
2018-05-11 10:52:58 +02:00
committed by GitHub
parent 5a00206826
commit f3f70cb13d
3 changed files with 179 additions and 2 deletions
+1
View File
@@ -65,6 +65,7 @@
<program name="ADS-B Intruders receiver" command="sw/ground_segment/misc/sbs2ivy">
<arg flag="--ac" constant="@AC_ID"/>
</program>
<program name="OpenSky-Network Intruders" command="sw/ground_segment/python/opensky-network/opensky.py"/>
<program name="SVInfo" command="sw/ground_segment/python/svinfo/svinfo.py"/>
<program name="IridiumDialer" command="sw/tools/iridium/iridium_link.py"/>
<program name="PayloadForward" command="sw/ground_segment/python/payload_forward/payload.py"/>
+2 -2
View File
@@ -51,11 +51,11 @@ let update_intruder = fun id wgs84 heading alt speed climb time ->
let intruder_exist = fun id ->
Hashtbl.mem intruders id
(* remove old intruders after 10s *)
(* remove old intruders after 20s *)
let remove_old_intruders = fun () ->
Hashtbl.iter
(fun id i ->
if (Unix.gettimeofday () -. i.last_update) > 10.0 then
if (Unix.gettimeofday () -. i.last_update) > 20.0 then
remove_intruder id
) intruders
+176
View File
@@ -0,0 +1,176 @@
#!/usr/bin/env python
#
# Copyright (C) 2018 Gautier Hattenberger <gautier.hattenberger@enac.fr>
#
# This file is part of paparazzi.
#
# paparazzi is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# paparazzi is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with paparazzi; see the file COPYING. If not, see
# <http://www.gnu.org/licenses/>.
#
'''
Get air traffic information from the opensky-network system (https://opensky-network.org/)
The python API is required and should be installed using the instruction
from https://github.com/openskynetwork/opensky-api
Positions of the UAVs are used to request for the surrounding aircraft
and INTRUDER messages are sent to the IVY bus for display in the GCS
'''
from __future__ import print_function
import sys
from os import path, getenv
from time import sleep, time
from opensky_api import OpenSkyApi
# if PAPARAZZI_HOME not set, then assume the tree containing this
# file is a reasonable substitute
PPRZ_HOME = getenv("PAPARAZZI_HOME", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../..')))
sys.path.append(PPRZ_HOME + "/var/lib/python")
from pprzlink.ivy import IvyMessagesInterface
from pprzlink.message import PprzMessage
class OpenSkyTraffic(object):
def __init__(self, period=10., margin=1., timeout=60., verbose=False):
self.period = period
self.margin = margin
self.timeout = timeout
self.last_receive = time()
self.verbose = verbose
self._interface = IvyMessagesInterface("OpenSkyTraffic")
self._opensky = OpenSkyApi()
self.ac_list = {}
self.intruder_list = {}
self.running = False
if self.verbose:
print("Starting opensky interface...")
# subscribe to FLIGHT_PARAM ground message
self._interface.subscribe(self.update_ac, PprzMessage("ground", "FLIGHT_PARAM"))
def stop(self):
if self.verbose:
print("Shutting down opensky interface...")
self.running = False
if self._interface is not None:
self._interface.shutdown()
def __del__(self):
self.stop()
def update_ac(self, ac_id, msg):
# update A/C info
self.last_receive = time()
self.ac_list[ac_id] = (self.last_receive, float(msg['lat']), float(msg['long']))
def filter_ac(self):
# purge timeout A/C
timeout = time() - self.timeout
for ac, (t, lat, lon) in self.ac_list.items():
if t < timeout:
del self.ac_list[ac]
def compute_bbox_list(self):
bbox = []
# for now assume that all UAVs are close enough, so merge all boxes into one
# future improvement could handle groups of UAVs far appart
lat_min, lat_max, lon_min, lon_max = None, None, None, None
for ac, (t, lat, lon) in self.ac_list.items():
if lat_min is None:
lat_min = lat
lat_max = lat
lon_min = lon
lon_max = lon
else:
lat_min = min(lat, lat_min)
lat_max = max(lat, lat_max)
lon_min = min(lon, lon_min)
lon_max = max(lon, lon_max)
if lat_min is not None:
bbox.append((lat_min-self.margin, lat_max+self.margin, lon_min-self.margin, lon_max+self.margin))
return bbox
def get_intruders(self):
self.filter_ac()
bbox = self.compute_bbox_list()
# request surounding aircraft to opensky-network
self.intruder_list = {}
for bb in bbox:
states = self._opensky.get_states(bbox=bb)
if states is not None:
for s in states.states:
self.intruder_list[s.callsign] = s
def send_intruder_msgs(self):
self.get_intruders()
def val_or_default(val, default):
if val is None:
return default
else:
return val
for i in self.intruder_list:
intruder = self.intruder_list[i]
if intruder.callsign is not None and len(intruder.callsign) > 0:
msg = PprzMessage("ground", "INTRUDER")
msg['id'] = intruder.icao24
msg['name'] = intruder.callsign.replace(" ", "")
msg['lat'] = int(intruder.latitude * 1e7)
msg['lon'] = int(intruder.longitude * 1e7)
msg['alt'] = int(val_or_default(intruder.geo_altitude, 0.) * 1000.)
msg['course'] = val_or_default(intruder.heading, 0.)
msg['speed'] = val_or_default(intruder.velocity, 0.)
msg['climb'] = val_or_default(intruder.vertical_rate, 0.)
msg['itow'] = intruder.time_position
if self.verbose:
print(msg)
self._interface.send(msg)
def run(self):
try:
self.running = True
t = 0.
while self.running:
t = t + 0.1 # increment timer until next update time is reach
if t > self.period:
self.send_intruder_msgs()
t = 0.
sleep(0.1) # wait a short time
except KeyboardInterrupt:
self.stop()
except Exception as e:
print("Failing with: "+str(e))
self.stop()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description="OpenSky-Network traffic information")
parser.add_argument('-p', '--period', dest='period', default=10., type=float, help="update period")
parser.add_argument('-m', '--margin', dest='margin', default=1., type=float, help="margin in degree to define the bounding box")
parser.add_argument('-t', '--timeout', dest='timeout', default=60., type=float, help="timeout to remove A/C")
parser.add_argument('-v', '--verbose', dest='verbose', default=False, action='store_true', help="display debug messages")
args = parser.parse_args()
osn = OpenSkyTraffic(args.period, args.margin, args.timeout, args.verbose)
osn.run()