Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code to improve performance and make heading bugs independant. #18

Merged
merged 1 commit into from
Sep 11, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions heads_up_display.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,19 @@ def run(self):
while self.tick(clock):
pass
finally:
self.__connection_manager__.shutdown()
pygame.display.quit()

sys.exit()
print('Shutting down Connection Manager')
self.__connection_manager__.shutdown()

print('Shutting down HTTP')
self.web_server.stop()

RecurringTask.kill_all()

print('Finished with run()')

return 0

def __render_view_title__(self, text):
try:
Expand Down Expand Up @@ -170,8 +179,10 @@ def tick(self, clock):
# to overdraw the pitch lines
# and improve readability
self.log("---- VIEW RENDER START ----")
for hud_element in view:
self.__render_view_element__(hud_element, orientation)

[self.__render_view_element__(
hud_element, orientation) for hud_element in view]

self.log("---------------------------")
except Exception as e:
self.warn("LOOP:" + str(e))
Expand Down Expand Up @@ -441,8 +452,9 @@ def __init__(self, logger):
if self.__logger__ is not None:
logger = self.__logger__.logger

web_server = restful_host.HudServer()
RecurringTask("rest_host", 0.1, web_server.run, start_immediate=False)
self.web_server = restful_host.HudServer()
RecurringTask("rest_host", 0.1, self.web_server.run,
start_immediate=False)
RecurringTask("purge_old_traffic", 10.0,
self.__purge_old_reports__, start_immediate=False)
RecurringTask("update_traffic", 0.1,
Expand Down Expand Up @@ -485,9 +497,11 @@ def __handle_input__(self):
bool -- True if the loop should continue, False if it should quit.
"""

for event in pygame.event.get():
if not self.__handle_key_event__(event):
return False
events = pygame.event.get()
event_handling_repsonses = map(self.__handle_key_event__, events)

if False in event_handling_repsonses:
return False

self.__clamp_view__()

Expand Down
7 changes: 2 additions & 5 deletions lib/colors.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,8 @@ def get_color_mix(left_color, right_color, proportion):
if array_length != len(right_color):
return left_color

new_color = []

for index in range(0, array_length):
new_color.append(
int(interpolate(left_color[index], right_color[index], proportion)))
indices = range(0, array_length)
new_color = [int(interpolate(left_color[index], right_color[index], proportion)) for index in indices]

return new_color

Expand Down
53 changes: 40 additions & 13 deletions lib/recurring_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,37 @@ class RecurringTask(object):
Object to control and handle a recurring task.
"""

def stop(self):
try:
if self.__last_task__ is not None:
self.pause()
self.__last_task__.cancel()
__SPAWNED_TASKS__ = []

return True
except:
return False
@staticmethod
def kill_all():
timeout_sec = 5
for task in RecurringTask.__SPAWNED_TASKS__: # list of your processes
print('Killing task {}'.format(task.__task_name__))

try:
task.stop()
except Exception as ex:
print('While shutting down EX:{}'.format(ex))

def stop(self):
self.__lock__.acquire()
self.__is_alive__ = False
self.__is_running__ = False
self.__lock__.release()

def is_running(self):
"""
Returns True if the task is running.
"""

return self.__task_callback__ is not None and self.__is_running__
self.__lock__.acquire()
result = self.__is_alive__ \
and self.__task_callback__ is not None \
and self.__is_running__
self.__lock__.release()

return result

def start(self):
"""
Expand All @@ -49,8 +64,9 @@ def pause(self):
Pauses the task if it is running.
"""

if self.is_running():
self.__is_running__ = False
self.__lock__.acquire()
self.__is_running__ = False
self.__lock__.release()

def __run_task__(self):
"""
Expand All @@ -60,10 +76,13 @@ def __run_task__(self):
self.__last_task__ = threading.Thread(target=self.__run_loop__)
self.__last_task__.start()

RecurringTask.__SPAWNED_TASKS__.append(self)

def __run_loop__(self):
while True:
while self.__is_alive__:
if self.__is_running__ and self.__task_callback__ is not None:
try:
self.__lock__.acquire()
self.__task_callback__()
except Exception as e:
# + sys.exc_info()[0]
Expand All @@ -73,8 +92,14 @@ def __run_loop__(self):
self.__logger__.info(error_mesage)
else:
print(error_mesage)
finally:
self.__lock__.release()

time.sleep(int(self.__task_interval__))
self.__lock__.acquire()
try:
time.sleep(int(self.__task_interval__) if self.__is_alive__ and self.__is_running__ else 0)
finally:
self.__lock__.release()

def __init__(self, task_name, task_interval, task_callback, logger=None, start_immediate=False):
"""
Expand All @@ -86,8 +111,10 @@ def __init__(self, task_name, task_interval, task_callback, logger=None, start_i
self.__task_interval__ = task_interval
self.__task_callback__ = task_callback
self.__logger__ = logger
self.__is_alive__ = True
self.__is_running__ = False
self.__last_task__ = None
self.__lock__ = threading.Lock()

if start_immediate:
self.start()
Expand Down
83 changes: 47 additions & 36 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,27 +18,27 @@ Using the "HUDLY Classic" projector and a Raspberry Pi 3.

Estimated cost is $300

* $40 for RaspberryPi 3
* $215 for HUDLY projector
* Fans, case, cables
- $40 for RaspberryPi 3
- $215 for HUDLY projector
- Fans, case, cables

Requires your aircraft to have a "12 Volt" cigarette power outlet.

### Alternative, Less Expensive Version

A self contained system that uses a 3D printed case and teleprompter glass. This version can be built for the cost of a Raspberry Pi and the 3D print.

*NOTE:* This version does have visibility issues in daylight conditions. The HUDLY Version is fully daylight visible.
_NOTE:_ This version does have visibility issues in daylight conditions. The HUDLY Version is fully daylight visible.

![Teleprompter Glass Version In Flight](media/in_flight.jpg)

Estimated Cost is $140

* $40 for a RaspberryPi 3
* $45 for the LCD screen
* $20 for Teleprompter Glass and shipping.
* Cost of 3D printing the special case.
* Cables
- $40 for a RaspberryPi 3
- $45 for the LCD screen
- $20 for Teleprompter Glass and shipping.
- Cost of 3D printing the special case.
- Cables

Can be powered by a USB powerbank or USB power.

Expand All @@ -47,7 +47,7 @@ Can be powered by a USB powerbank or USB power.
You may use a number pad as input device. I used velcro to secure the number pad to my dashboard.

| Key | Action |
|-----------|------------------------------------------------------------------------------|
| --------- | ---------------------------------------------------------------------------- |
| Backspace | Tell the Stratux that you are in a level position. Resets the AHRS to level. |
| + | Next view |
| - | Previous view |
Expand All @@ -57,10 +57,10 @@ You may use a number pad as input device. I used velcro to secure the number pad

## Views

* Traffic
* AHRS
* Traffic List (Text only)
* Blank
- Traffic
- AHRS
- Traffic List (Text only)
- Blank

### Traffic View

Expand All @@ -84,7 +84,7 @@ The closer the traffic, the larger the heading bug.

This shows you all of the traffic with reliable ADSB data.

*NOTE: Any planes further than 10SM are excluded from this view.*
_NOTE: Any planes further than 10SM are excluded from this view._

It attempts to prioritize traffic by distance.

Expand Down Expand Up @@ -121,9 +121,9 @@ We are experiencing 0.8Gs, less than normal gravity.

![Traffic View Screenshot](media/traffic_listing_view.jpg)

This shows us *at most* the five closest planes.
This shows us _at most_ the five closest planes.

*NOTE: Any planes further than 10SM are excluded from this view.*
_NOTE: Any planes further than 10SM are excluded from this view._

Here we see N435SP is the closest plane, bearing 345, 1.0 statute miles away, and 1,300' above us.

Expand All @@ -137,26 +137,26 @@ A blank screen so no information is displayed.

### All Builds

*NOTE:* This _does not_ include a power source. You will need to supply ship power from a 5V USB port or from a battery.
_NOTE:_ This _does not_ include a power source. You will need to supply ship power from a 5V USB port or from a battery.

* [Raspberry Pi 3](https://www.amazon.com/Raspberry-Pi-RASPBERRYPI3-MODB-1GB-Model-Motherboard/dp/B01CD5VC92/ref=sr_1_3?s=electronics&ie=UTF8&qid=1529215701&sr=1-3&keywords=raspberry+pi+3)
* [Case For Raspberry Pi](https://www.amazon.com/iPhoenix-Raspberry-White-Compatible-Model/dp/B06XQSXZ97/ref=sr_1_3?s=electronics&dd=iYEspjjyeRXfqDW9BHwJFw%2C%2C&ddc_refnmnt=pfod&ie=UTF8&qid=1529215794&sr=1-3&keywords=white+raspberry+pi+3+case&refinements=p_97%3A11292772011)
* [Cooling Fan for Raspberry Pi](https://www.amazon.com/gp/product/B075R4S9GH/ref=od_aui_detailpages00?ie=UTF8&psc=1)
* [Micro USB Cable](https://www.amazon.com/AmazonBasics-Male-Micro-Cable-Black/dp/B0711PVX6Z/ref=sr_1_6?s=electronics&ie=UTF8&qid=1529215888&sr=1-6&keywords=micro+usb+cable)
* [Micro SD Card](https://www.amazon.com/SanDisk-Ultra-Micro-Adapter-SDSQUNC-016G-GN6MA/dp/B010Q57SEE/ref=sr_1_10?s=pc&ie=UTF8&qid=1529215944&sr=1-10&keywords=micro+sd+card)
* [Rottay Mechanical Keypad](https://www.amazon.com/Number-Rottay-Mechanical-Numeric-backlit/dp/B076FTSY6J/ref=sr_1_3?ie=UTF8&qid=1529215627&sr=8-3&keywords=mechanical+keypad)
- [Raspberry Pi 3](https://www.amazon.com/Raspberry-Pi-RASPBERRYPI3-MODB-1GB-Model-Motherboard/dp/B01CD5VC92/ref=sr_1_3?s=electronics&ie=UTF8&qid=1529215701&sr=1-3&keywords=raspberry+pi+3)
- [Case For Raspberry Pi](https://www.amazon.com/iPhoenix-Raspberry-White-Compatible-Model/dp/B06XQSXZ97/ref=sr_1_3?s=electronics&dd=iYEspjjyeRXfqDW9BHwJFw%2C%2C&ddc_refnmnt=pfod&ie=UTF8&qid=1529215794&sr=1-3&keywords=white+raspberry+pi+3+case&refinements=p_97%3A11292772011)
- [Cooling Fan for Raspberry Pi](https://www.amazon.com/gp/product/B075R4S9GH/ref=od_aui_detailpages00?ie=UTF8&psc=1)
- [Micro USB Cable](https://www.amazon.com/AmazonBasics-Male-Micro-Cable-Black/dp/B0711PVX6Z/ref=sr_1_6?s=electronics&ie=UTF8&qid=1529215888&sr=1-6&keywords=micro+usb+cable)
- [Micro SD Card](https://www.amazon.com/SanDisk-Ultra-Micro-Adapter-SDSQUNC-016G-GN6MA/dp/B010Q57SEE/ref=sr_1_10?s=pc&ie=UTF8&qid=1529215944&sr=1-10&keywords=micro+sd+card)
- [Rottay Mechanical Keypad](https://www.amazon.com/Number-Rottay-Mechanical-Numeric-backlit/dp/B076FTSY6J/ref=sr_1_3?ie=UTF8&qid=1529215627&sr=8-3&keywords=mechanical+keypad)

### Recommended HUDLY Build

* [HUDLY Classic](https://gethudly.com/classic)
* *NOTE:* You will most likely want to order the cigarette lighter powered version.
* *NOTE:* You can order the iPhone or Android cable option based on your phone. It will not matter for the StratuxHud.
* [12" HDMI Cable](https://www.amazon.com/StarTech-com-0-3m-Short-Speed-Cable/dp/B00K3HF276/ref=sr_1_11?s=electronics&ie=UTF8&qid=1529216822&sr=1-11&keywords=short%2Bhdmi%2Bcable&th=1)
- [HUDLY Classic](https://gethudly.com/classic)
- _NOTE:_ You will most likely want to order the cigarette lighter powered version.
- _NOTE:_ You can order the iPhone or Android cable option based on your phone. It will not matter for the StratuxHud.
- [12" HDMI Cable](https://www.amazon.com/StarTech-com-0-3m-Short-Speed-Cable/dp/B00K3HF276/ref=sr_1_11?s=electronics&ie=UTF8&qid=1529216822&sr=1-11&keywords=short%2Bhdmi%2Bcable&th=1)

### 3D Print Build

* [Teleprompter Glass Sample of both thickness of the 60/40 glass](<https://telepromptermirror.com/sample/>)
* [SunFounder 5" TFT LCD](https://www.amazon.com/SunFounder-Monitor-Display-800X480-Raspberry/dp/B01HXSFIH6)
- [Teleprompter Glass Sample of both thickness of the 60/40 glass](https://telepromptermirror.com/sample/)
- [SunFounder 5" TFT LCD](https://www.amazon.com/SunFounder-Monitor-Display-800X480-Raspberry/dp/B01HXSFIH6)

## Install instructions

Expand All @@ -182,6 +182,17 @@ A blank screen so no information is displayed.
18. "Other" -> "English US" -> "Default" -> "No compose" -> "Yes"
19. "Finish"

#### Raspberry Pi 3B+

If you are using a 3B+, it may suffer from undervoltage alerts.
These may be relieved by the following command to update your Linux install to the latest:

```bash
sudo apt-get update && sudo apt-get dist-upgrade -y
```

Make sure you are using a high quality power cable if you are using a Pi 3B+

### Install Software

1. Enter `ping google.com`. Press ctrl+c after a while. This will confirm that you have internet access. If you do not, then use rasp-config to re-enter your wi-fi
Expand All @@ -200,9 +211,9 @@ A blank screen so no information is displayed.
14. Select "Nano" (Option 1)
15. Enter the following text at the _bottom_ of the file:

```code
@reboot sudo python /home/pi/StratuxHud/stratux_hud.py &
```
```bash
@reboot sudo python /home/pi/StratuxHud/stratux_hud.py &
```

1. Save and quit.

Expand Down Expand Up @@ -233,9 +244,9 @@ The initial project was inspired by Kris Knigga's PyAhrs project <https://github

The following components are used:

* Python
* PyGame
* Ws4Py
- Python
- PyGame
- Ws4Py

... and of course Stratux

Expand Down
5 changes: 5 additions & 0 deletions restful_host.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ def run(self):
print("localhost = {}:{}".format(self.__local_ip__, self.__port__))

self.__httpd__.serve_forever()

def stop(self):
if self.__httpd__ is not None:
self.__httpd__.shutdown()
self.__httpd__.server_close()

def __init__(self):
self.__port__ = RESTFUL_HOST_PORT
Expand Down
Loading