You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+51-14
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,5 @@
1
1
# skyportal
2
-
A PyPortal based flight tracker powered by [Adafruit](https://io.adafruit.com/), [Geoapify](https://www.geoapify.com/), and [The OpenSky Network](https://opensky-network.org/).
2
+
A PyPortal based flight tracker powered by [Adafruit](https://io.adafruit.com/), [Geoapify](https://www.geoapify.com/), [ADSB.lol](https://adsb.lol), and [The OpenSky Network](https://opensky-network.org/).
3
3
4
4
Heavily inspired by Bob Hammell"s PyPortal Flight Tracker ([GH](https://github.com/rhammell/pyportal-flight-tracker), [Tutorial](https://www.hackster.io/rhammell/pyportal-flight-tracker-0be6b0#story)).
5
5
@@ -50,24 +50,61 @@ secrets = {
50
50
"aio_username" : "YOUR_AIO_USERNAME",
51
51
"aio_key" : "YOUR_AIO_KEY",
52
52
# Open Sky Network credentials, for getting flight information
53
+
# Can be omitted if not using OpenSky
53
54
"opensky_username": "YOUR_OPENSKY_USERNAME",
54
-
"opensky_password": "YOUR_OPENSKY_PASSWORD"
55
+
"opensky_password": "YOUR_OPENSKY_PASSWORD",
56
+
# Proxy API Gateway credentials
57
+
# Can be omitted if not using a proxy server
58
+
"proxy_api_url": "YOUR_PROXY_API_URL",
59
+
"proxy_api_key": "YOUR_PROXY_API_KEY",
55
60
}
56
61
```
57
62
58
63
#### Skyportal Configuration
59
64
A collection of functionality-related constants is specified in `skyportal_config.py`, which can be adjusted to suit your needs:
|`USE_DEFAULT_MAP`| Use the default map image rather than query Geoapify |`False`|
69
+
|`MAP_CENTER_LAT`| Map center latitude, decimal degrees |`42.41`|
70
+
|`MAP_CENTER_LON`| Map center longitude, deimal degrees |`-71.17`|
71
+
|`GRID_WIDTH_MI`| Map grid width, miles |`15`|
72
+
|`AIRCRAFT_DATA_SOURCE`| Aircraft State API to utilize<sup>1</sup> |`opensky`|
73
+
|`SKIP_GROUND`| Skip drawing aircraft on the ground |`True`|
74
+
|`GEO_ALTITUDE_THRESHOLD_M`| Skip drawing aircraft below this GPS altitude, meters |`20`|
75
+
76
+
**Notes:**
77
+
1. See [Data Sources](#data-sources) for valid options
78
+
79
+
## Data Sources
80
+
### OpenSky-Network - `"opensky"`
81
+
Query the [OpenSky Network](https://opensky-network.org/) API. This requires a user account to be created & credentials added to `secrets.py`.
82
+
83
+
Information on their REST API can be found [here](https://openskynetwork.github.io/opensky-api/rest.html).
84
+
85
+
### ADSB.lol - `"adsblol"`
86
+
Query the [ADSB.lol](https://adsb.lol/). This currently does not require user authentication.
87
+
88
+
Information on their REST API can be found [here](https://api.adsb.lol/docs).
89
+
90
+
**NOTE:** This API provides a lot of interesting information in the state vector provided for each aircraft. Depending on the level of congestion in your query area, may be more data than can fit into RAM (See: [Known Limitations](#known-limitations)).
91
+
92
+
### Proxy API - `"proxy"`
93
+
Query a user-specified proxy server using the URL and API key provided in `secrets.py`.
94
+
95
+
For authentication, the API is assumed to expect an key provided in the `"x-api-key"` header.
96
+
97
+
The proxy API is assumed to expect three parameters:
98
+
* `lat`, center latitude, decimal degrees
99
+
* `lon`, denter longitude, decimal degrees
100
+
* `radius`, search radius, miles
101
+
102
+
The proxy API is expected to return two parameters:
103
+
* `"ac"` - A list of state vectors, as dictionaries, whose kv pairs map directly to `skyportal.aircraftlib.AircraftState`
104
+
* `"api_time"` - UTC epoch time, seconds, may be a float
105
+
106
+
An example using ADSB.lol and AWS Lambda is provided by this repository in [`./adsblol-proxy`](./adsblol-proxy/README.md)
107
+
71
108
72
109
## Touchscreen Functionality
73
110
**NOTE:** Touchscreen input is mostly limited to one touch event per screen tap, rather than continuously firing while the screen is being touched.
@@ -77,5 +114,5 @@ A collection of functionality-related constants is specified in `skyportal_confi
77
114
### Aircraft Information
78
115
Tapping on an aircraft icon will display state information for the aircraft closest to the registered touch point.
79
116
80
-
### Screenshot
81
-
If enabled in the SkyPortal configuration file, a screenshot button is created in the lower left, allowing the user to take a screenshot to SD card storage. The device utilizes a rolling storage, keeping the `n` most recent screenshots and discarding the oldest screenshot if above this threshold.
117
+
##Known Limitations
118
+
The PyPortal is a highly memory constrained environment, which presents challenges when aiming to create a highly expressive UI. While every attempt is being made to minimize memory usage to keep the Skyportal functioning, the device may occasionally run out of memory. The most likely point for this to happen is when receiving the web request with aircraft state information from your API of choice. Depending on how congested your selected airspace is at query time, there may simply be too much information provided by the API for the device to handle & I've intentionally left the exception unhandled so it will crash the device. Should you find this ocurring often, you may be interested in setting up a proxy server to return only the information needed for the device to function, which can significantly alleviate the amount of RAM needed. See [Proxy API](#proxy-api---proxy) for more information.
The enclosed `adsblol_proxy.py` file was developed to be deployed using an [AWS Lambda](https://aws.amazon.com/lambda/) behind an [AWS API Gateway](https://aws.amazon.com/api-gateway/). For the default Skyportal configuration this should fall well within the monthly limits of the AWS Free Tier.
3
+
4
+
If, like me, you've never used AWS before this point, the following steps should help you get up and running. If you know what you're doing then you can probably figure all this out without my help.
5
+
6
+
## AWS Lambda
7
+
### Create Function
8
+
From the Lambda console, create a new function with the following configuration:
9
+
* Author from scratch
10
+
* Whatever function name you want
11
+
* Python 3.11 Runtime
12
+
* x86_64 architecture
13
+
14
+
Once created, edit your Runtime Settings and change the Handler to `adsblol_proxy.lambda_handler`.
15
+
16
+
### Create a `.zip` deployment
17
+
Our Lambda depends on [`httpx`](https://github.com/encode/httpx/) to make its web request, so it will need to be installed along with its dependencies before we can deploy. One way to achieve this with Lambda is to upload a [`.zip` deployment package](https://docs.aws.amazon.com/lambda/latest/dg/python-package.html#python-package-create-dependencies); the documentation can be a little obtuse but ultimately the goal is to end up with a zip file whose contents look something like this:
18
+
19
+
```
20
+
anyio/
21
+
anyio-4.0.0.dist-info/
22
+
certifi/
23
+
certifi-2023.7.22.dist-info/
24
+
h11/
25
+
h11-0.14.0.dist-info/
26
+
httpcore/
27
+
httpcore-1.0.2.dist-info/
28
+
httpx/
29
+
httpx-0.25.1.dist-info/
30
+
idna/
31
+
idna-3.4.dist-info/
32
+
sniffio/
33
+
sniffio-1.3.0.dist-info/
34
+
adsblol_proxy.py
35
+
```
36
+
37
+
I accomplished this using a virtual environment, e.g.:
38
+
39
+
```
40
+
$ python -m venv ./.venv
41
+
$ source ./.venv/Scripts/activate
42
+
$ python -m pip install -U pip httpx
43
+
```
44
+
45
+
Move or copy the everything from `./.venv/Lib/site-packages`**EXCEPT**`pip` (blows up the file size unnecessarily & we don't need it) into the directory with `adsblol_proxy.py` and zip everything together so you get the layout above. You can then upload this zip file to Lambda & then deploy the code.
46
+
47
+
## AWS API Gateway
48
+
### Create API
49
+
From the API Gateway console, create a new API:
50
+
* REST API
51
+
* New API
52
+
* Whatever name you'd like
53
+
* Optional description
54
+
* Regional endpoint type
55
+
56
+
### Create Method
57
+
Under resources, create a new method:
58
+
*`GET` method type
59
+
* Lambda function integration type
60
+
* Enable "Lambda proxy integration"
61
+
* If you've already created your Lambda function above, you should be able to select it
62
+
* Default timeout should be fine
63
+
64
+
### Edit Method
65
+
Edit your method request settings:
66
+
* Authorization - None
67
+
* Request validator - None
68
+
* API key required - CHECK
69
+
* URL query string parameters
70
+
*`lat`, required
71
+
*`lon`, required
72
+
*`radius`, required
73
+
74
+
Once this is done, Deploy your API. You will need to specify a stage name if you haven't previously. Since you're just doing a hobby API you can name it whatever you want; I called mine `live`.
75
+
76
+
Make a note of your Invoke URL, which can be found under Stages. It will be something like `https://abcd123.execute-api.us-east-69.amazonaws.com/live/`.
77
+
78
+
### Create an API key
79
+
Under API Keys create a new API key & store in a secure location that you can access later.
80
+
81
+
### Create a Usage Plan
82
+
This must be created in order for the API key to work. Fill out the options however you'd like.
83
+
84
+
Once this is created you'll need to add a stage, this is what you targeted when you deployed your API.
85
+
86
+
Finally, you'll need to add your API key to the Associated API keys.
87
+
88
+
## Testing
89
+
You can check that your API is functional using `curl`:
To utilize the proxy server, copy your Invoke URL and API key into `secrets.py`, and set `AIRCRAFT_DATA_SOURCE = "proxy"` in your `skyportal_config.py`.
0 commit comments