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

mavproxy_soar: Add initial soaring display #1500

Merged
merged 1 commit into from
Jan 16, 2025
Merged
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
96 changes: 96 additions & 0 deletions MAVProxy/modules/mavproxy_soar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env python3
'''
Soaring Module
Ryan Friedman

This module displays the estimated soaring thermals on the map.
A circle is drawn with the estimated radius from ArduSoar's estimated thermal location.

Ryanf55 marked this conversation as resolved.
Show resolved Hide resolved
Note that this module uses NAMED_VALUE_FLOAT messages from the autopilot,
a temporary measure until a proper mavlink message is decided upon.

AP_FLAKE8_CLEAN
'''

from MAVProxy.modules.lib import mp_module, mp_util
from MAVProxy.modules.mavproxy_map import mp_slipmap

import time


class soar(mp_module.MPModule):
_SOAR_THERMAL_MAP_OBJ_ID = "soar-thermal"
_CLEAR_STALE_THERMAL_TIME = 5.0

def __init__(self, mpstate):
"""Initialise module"""
super(soar, self).__init__(mpstate, "soar", "")
self._strength = None
self._radius = None
self._x = None
self._y = None
self._last_draw_time = time.time()

def mavlink_packet(self, m):
Ryanf55 marked this conversation as resolved.
Show resolved Hide resolved
'''handle mavlink packets'''

if m.get_type() == 'NAMED_VALUE_FLOAT' and m.name.startswith("SOAR"):
if m.name == "SOAREKFX0":
self._strength = m.value
elif m.name == "SOAREKFX1":
self._radius = m.value
elif m.name == "SOAREKFX2":
self._x = m.value
elif m.name == "SOAREKFX3":
self._y = m.value
else:
raise NotImplementedError(m.name)

self.draw_thermal_estimate()

def idle_task(self):
'''called rapidly by mavproxy'''

if time.time() - self._last_draw_time > self._CLEAR_STALE_THERMAL_TIME:
self.clear_thermal_estimate()

def draw_thermal_estimate(self):

if self._radius is None:
return
if self._x is None:
return
if self._y is None:
return

wp_module = self.module('wp')
if wp_module is None:
return
home = wp_module.get_home()
if home is None:
return

home_lat = home.x
home_lng = home.y

(thermal_lat, thermal_lon) = mp_util.gps_offset(home_lat, home_lng, self._y, self._x)

slipcircle = mp_slipmap.SlipCircle(
self._SOAR_THERMAL_MAP_OBJ_ID, # key
"thermals", # layer
(thermal_lat, thermal_lon), # latlon
self._radius, # radius
(0, 255, 255),
linewidth=2)
for mp in self.module_matching('map*'):
self._last_draw_time = time.time()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking timeout might be based on message retrieval time rather than draw time.

That way when the vehicle loses the thermal the GCS gets updated to remove the thermal hint

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking timeout might be based on message retrieval time rather than draw time.

That way when the vehicle loses the thermal the GCS gets updated to remove the thermal hint

The estimator stops running once you exit thermal mode when you lose the thermal - you stop getting packets. Functionally, message receipt calls draw, so it's the same time anyways.

mp.map.add_object(slipcircle)

def clear_thermal_estimate(self):
for mp in self.module_matching('map*'):
mp.map.remove_object(self._SOAR_THERMAL_MAP_OBJ_ID)


def init(mpstate):
'''initialise module'''
return soar(mpstate)