-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #32 from Cloud-RF/adsb
ADSB demo by OrinjCaike
- Loading branch information
Showing
3 changed files
with
205 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
import simplekml | ||
from urllib.request import Request, urlopen | ||
import requests | ||
import json | ||
from time import sleep | ||
from math import atan, pi | ||
|
||
requests.packages.urllib3.disable_warnings() # dissable warnings from API requests. make terminal clearer | ||
|
||
########### CONSTANTS DO NOT CHANGE ########### | ||
|
||
FEET_TO_METRES_CONSTANT = 0.3048 # ADSB works in feat, KML works in metres | ||
RF_API_SERVER = "https://api.cloudrf.com" # CloudRF API | ||
RF_API_KEY = "" # API key for CloudRF contained in api-key.txt | ||
ADSB_API_SERVER = "https://api.adsb.lol/v2/callsign/" # ADSB API | ||
REQUEST_HEADERS = {"User-Agent": "Mozilla/5.0"} # dont look like a bot | ||
|
||
############################################### | ||
|
||
|
||
######### FEEL FREE TO CHANGE SETTINGS ######### | ||
|
||
SIMULATION_RESOLUTION = 90 # resolution in metres | ||
SIMULATION_RADIUS = 90 # simulation radius in metres | ||
|
||
SLEEP_TIME = 2 # seconds between each update, 2 recommended | ||
|
||
################################################ | ||
|
||
callsign = input("Enter callsign for plane: ") # get callsign for ADSB API | ||
|
||
lon = 0 | ||
lat = 0 | ||
|
||
prevlat = 0 | ||
prevlon = 0 | ||
|
||
with open("api-key.txt") as file: | ||
RF_API_KEY = file.read().rstrip() | ||
|
||
while True: | ||
|
||
kml = simplekml.Kml() | ||
|
||
# make request to ADSB API for plane data | ||
req = Request( | ||
url = ADSB_API_SERVER + callsign, | ||
headers = REQUEST_HEADERS | ||
) | ||
|
||
planeData = json.load(urlopen(req)) # load JSON from ADSB request | ||
|
||
if len(planeData["ac"]) > 0: # check if plane has position data | ||
|
||
alt = 0 | ||
|
||
if (planeData["ac"][0]["alt_baro"]) != "ground": # check plane is not grounded | ||
alt = float(planeData["ac"][0]["alt_baro"]) # update altitude | ||
|
||
alt *= FEET_TO_METRES_CONSTANT # ADSB works in feat, KML works in metres | ||
|
||
# update previous latitude and longditude if the plane has moved | ||
if float(planeData["ac"][0]["lat"]) != lat: prevlat = lat | ||
if float(planeData["ac"][0]["lon"]) != lon: prevlon = lon | ||
|
||
# update current latitude and longditude | ||
lat = float(planeData["ac"][0]["lat"]) | ||
lon = float(planeData["ac"][0]["lon"]) | ||
|
||
# calculate change in longditude and latitude for heading calculation | ||
dlat = lat - prevlat | ||
dlon = lon - prevlon | ||
|
||
heading = 0 | ||
|
||
if dlat != 0: # check for zeros in denominator | ||
heading = atan(dlon / dlat) # calculate heading | ||
heading *= 180 / pi # convert radians to degrees | ||
|
||
while (heading < 0): heading += 360 # check for negative heading | ||
|
||
data = { # parameters sent to CloudRF API | ||
"site": "A1", | ||
"engine": 1, | ||
"network": "Testing", | ||
"transmitter": { | ||
"lat": lat, | ||
"lon": lon, | ||
"alt": alt, | ||
"frq": 123, | ||
"txw": 10, | ||
"bwi": 0.1 | ||
}, | ||
"model": { | ||
"pm": 7, | ||
"pe": 2, | ||
"ked": 0 | ||
}, | ||
"receiver": { | ||
"lat": 0, | ||
"lon": 0, | ||
"alt": 2, | ||
"rxg": 2, | ||
"rxs": -100 | ||
}, | ||
"antenna": { | ||
"txg": 21, | ||
"txl": 1, | ||
"ant": 0, | ||
"azi": int(heading), | ||
"tlt": 45, # Downtilt towards the earth! | ||
"hbw": 120, | ||
"vbw": 120, | ||
"pol": "v", | ||
"fbr": 40 | ||
}, | ||
"environment": { | ||
"elevation": 2, # 2 = DTM | ||
"landcover": 0, | ||
"buildings": 0 | ||
}, | ||
"output": { | ||
"units": "metric", | ||
"col": "RAINBOW.dBm", | ||
"out": 2, | ||
"ber": 2, | ||
"mod": 7, | ||
"nf": -100, | ||
"res": SIMULATION_RESOLUTION, | ||
"rad": SIMULATION_RADIUS | ||
} | ||
} | ||
|
||
# make request to CloudRF API | ||
resp = requests.post(RF_API_SERVER + "/area", | ||
json = data, | ||
headers = {'key': RF_API_KEY}, | ||
verify = False | ||
) | ||
|
||
if "elapsed" in resp.json(): # if a heatmap has been returned | ||
netlink = kml.newnetworklink(name = "Coverage") | ||
netlink.link.href = resp.json()["kmz"] | ||
netlink.link.viewrefreshmode = simplekml.ViewRefreshMode.onrequest | ||
|
||
print(str(resp.json()["elapsed"]) + "ms elapsed calculating heatmap. Press Ctrl+C to stop") | ||
|
||
else: print(resp.json()) # see why no heatmap was returned / what was returned instead | ||
|
||
point = kml.newpoint(name = "Plane " + callsign, coords = [(lon, lat, alt)]) # lon, lat, optional height | ||
point.altitudemode = simplekml.AltitudeMode.absolute # altitude relative to sea level | ||
point.extrude = 1 # draw line from plane to ground | ||
|
||
kml.save("lineofsight.kml") # save pin and heatmap to kml file | ||
|
||
sleep(SLEEP_TIME) # sleep to not spam CloudRF | ||
|
||
else: | ||
print("Plane dose not have position data, try another plane") | ||
break |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
# Automatic Dependent Surveillance-Broadcast (ADS-B) demo | ||
|
||
## Background | ||
|
||
ADS-B is a surveillance technology for positioning aircraft that is more precise than radar. ADS-B broadcasts information about an aircraft's location, altitude and speed to ground stations every second. This allows the tracking of aircraft through APIs like [ADSB.lol](https://adsb.lol). | ||
|
||
[CloudRF](https://cloudrf.com) allows the simulation of radio communications. When combined, these APIs allow a live line of sight or RF calculation of any aircraft's position. | ||
|
||
## Demo | ||
|
||
![DEMO](demo.jpg) | ||
|
||
Pick a plane from [ADSB.lol](https://adsb.lol) and enter it's asociated **callsign** into the python script in this demo. The script will then send a request to ADSB.lol for the plane data, send a request to CloudRF for heatmap data of where the plane can see, using a radio simulation. | ||
|
||
It will then save a KML file containing a placemark for the plane's location and the coverage heatmap. This kml file can be loaded into Google Earth to view the plane, as a pin, and its line of sight, as a heatmap. | ||
|
||
To make the layer refresh periodically, add it as a network KML layer with a time based refresh with a period of 3 seconds. | ||
|
||
### Requirements | ||
|
||
1. A CloudRF API key in a file named api-key.txt | ||
|
||
2. A KML viewer like [Google Earth](https://earth.google.com/intl/earth/download/ge/agree.html) | ||
|
||
3. [Python3](https://www.python.org/downloads/) | ||
|
||
4. python packages simplekml, urllib, requests | ||
|
||
### Installation | ||
|
||
pip3 install simplekml requests | ||
|
||
### Operation | ||
|
||
python3 ADSB_demo.py [Call sign] | ||
|
||
## References | ||
|
||
### ADSB | ||
|
||
https://adsb.lol | ||
|
||
### CloudRF | ||
|
||
https://cloudrf.com |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.