Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/Tools/mavproxy_modules/lib/magcal_graph_ui.py
Views: 1799
# Copyright (C) 2016 Intel Corporation. All rights reserved.1#2# This file is free software: you can redistribute it and/or modify it3# under the terms of the GNU General Public License as published by the4# Free Software Foundation, either version 3 of the License, or5# (at your option) any later version.6#7# This file is distributed in the hope that it will be useful, but8# WITHOUT ANY WARRANTY; without even the implied warranty of9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.10# See the GNU General Public License for more details.11#12# You should have received a copy of the GNU General Public License along13# with this program. If not, see <http://www.gnu.org/licenses/>.14import matplotlib.pyplot as plt15from matplotlib.backends.backend_wxagg import FigureCanvas16from mpl_toolkits.mplot3d import Axes3D17from mpl_toolkits.mplot3d.art3d import Poly3DCollection18from pymavlink.mavutil import mavlink1920from MAVProxy.modules.lib import wx_processguard21from MAVProxy.modules.lib.wx_loader import wx2223import geodesic_grid as grid2425class MagcalPanel(wx.Panel):26_status_markup_strings = {27mavlink.MAG_CAL_NOT_STARTED: 'Not started',28mavlink.MAG_CAL_WAITING_TO_START: 'Waiting to start',29mavlink.MAG_CAL_RUNNING_STEP_ONE: 'Step one',30mavlink.MAG_CAL_RUNNING_STEP_TWO: 'Step two',31mavlink.MAG_CAL_SUCCESS: '<span color="blue">Success</span>',32mavlink.MAG_CAL_FAILED: '<span color="red">Failed</span>',33}3435_empty_color = '#7ea6ce'36_filled_color = '#4680b9'3738def __init__(self, *k, **kw):39super(MagcalPanel, self).__init__(*k, **kw)4041facecolor = self.GetBackgroundColour().GetAsString(wx.C2S_HTML_SYNTAX)42fig = plt.figure(facecolor=facecolor, figsize=(1,1))4344self._canvas = FigureCanvas(self, wx.ID_ANY, fig)45self._canvas.SetMinSize((300,300))4647self._id_text = wx.StaticText(self, wx.ID_ANY)48self._status_text = wx.StaticText(self, wx.ID_ANY)49self._completion_pct_text = wx.StaticText(self, wx.ID_ANY)5051sizer = wx.BoxSizer(wx.VERTICAL)52sizer.Add(self._id_text)53sizer.Add(self._status_text)54sizer.Add(self._completion_pct_text)55sizer.Add(self._canvas, proportion=1, flag=wx.EXPAND)56self.SetSizer(sizer)5758ax = fig.add_subplot(111, axis_bgcolor=facecolor, projection='3d')59self.configure_plot(ax)6061def configure_plot(self, ax):62extra = .563lim = grid.radius + extra64ax.set_xlim3d(-lim, lim)65ax.set_ylim3d(-lim, lim)66ax.set_zlim3d(-lim, lim)6768ax.set_xlabel('x')69ax.set_ylabel('y')70ax.set_zlabel('z')7172ax.invert_zaxis()73ax.invert_xaxis()7475ax.set_aspect('equal')7677self._polygons_collection = Poly3DCollection(78grid.sections_triangles,79edgecolors='#386694',80)81ax.add_collection3d(self._polygons_collection)8283def update_status_from_mavlink(self, m):84status_string = self._status_markup_strings.get(m.cal_status, '???')85self._status_text.SetLabelMarkup(86'<b>Status:</b> %s' % status_string,87)8889def mavlink_magcal_report(self, m):90self.update_status_from_mavlink(m)91self._completion_pct_text.SetLabel('')9293def mavlink_magcal_progress(self, m):94facecolors = []95for i, mask in enumerate(m.completion_mask):96for j in range(8):97section = i * 8 + j98if mask & 1 << j:99facecolor = self._filled_color100else:101facecolor = self._empty_color102facecolors.append(facecolor)103self._polygons_collection.set_facecolors(facecolors)104self._canvas.draw()105106self._id_text.SetLabelMarkup(107'<b>Compass id:</b> %d' % m.compass_id108)109110self._completion_pct_text.SetLabelMarkup(111'<b>Completion:</b> %d%%' % m.completion_pct112)113114self.update_status_from_mavlink(m)115116_legend_panel = None117@staticmethod118def legend_panel(*k, **kw):119if MagcalPanel._legend_panel:120return MagcalPanel._legend_panel121122p = MagcalPanel._legend_panel = wx.Panel(*k, **kw)123sizer = wx.BoxSizer(wx.HORIZONTAL)124p.SetSizer(sizer)125126marker = wx.Panel(p, wx.ID_ANY, size=(10, 10))127marker.SetBackgroundColour(MagcalPanel._empty_color)128sizer.Add(marker, flag=wx.ALIGN_CENTER)129text = wx.StaticText(p, wx.ID_ANY)130text.SetLabel('Sections not hit')131sizer.Add(text, border=4, flag=wx.ALIGN_CENTER | wx.LEFT)132133marker = wx.Panel(p, wx.ID_ANY, size=(10, 10))134marker.SetBackgroundColour(MagcalPanel._filled_color)135sizer.Add(marker, border=10, flag=wx.ALIGN_CENTER | wx.LEFT)136text = wx.StaticText(p, wx.ID_ANY)137text.SetLabel('Sections hit')138sizer.Add(text, border=4, flag=wx.ALIGN_CENTER | wx.LEFT)139return p140141class MagcalFrame(wx.Frame):142def __init__(self, conn):143super(MagcalFrame, self).__init__(144None,145wx.ID_ANY,146title='Magcal Graph',147)148149self.SetMinSize((300, 300))150151self._conn = conn152153self._main_panel = wx.ScrolledWindow(self, wx.ID_ANY)154self._main_panel.SetScrollbars(1, 1, 1, 1)155156self._magcal_panels = {}157158self._sizer = wx.BoxSizer(wx.VERTICAL)159self._main_panel.SetSizer(self._sizer)160161idle_text = wx.StaticText(self._main_panel, wx.ID_ANY)162idle_text.SetLabelMarkup('<i>No calibration messages received yet...</i>')163idle_text.SetForegroundColour('#444444')164165self._sizer.AddStretchSpacer()166self._sizer.Add(167idle_text,168proportion=0,169flag=wx.ALIGN_CENTER | wx.ALL,170border=10,171)172self._sizer.AddStretchSpacer()173174self._timer = wx.Timer(self)175self.Bind(wx.EVT_TIMER, self.timer_callback, self._timer)176self._timer.Start(200)177178def add_compass(self, id):179if not self._magcal_panels:180self._sizer.Clear(deleteWindows=True)181self._magcal_panels_sizer = wx.BoxSizer(wx.HORIZONTAL)182183self._sizer.Add(184self._magcal_panels_sizer,185proportion=1,186flag=wx.EXPAND,187)188189legend = MagcalPanel.legend_panel(self._main_panel, wx.ID_ANY)190self._sizer.Add(191legend,192proportion=0,193flag=wx.ALIGN_CENTER,194)195196self._magcal_panels[id] = MagcalPanel(self._main_panel, wx.ID_ANY)197self._magcal_panels_sizer.Add(198self._magcal_panels[id],199proportion=1,200border=10,201flag=wx.EXPAND | wx.ALL,202)203204def timer_callback(self, evt):205close_requested = False206mavlink_msgs = {}207while self._conn.poll():208m = self._conn.recv()209if isinstance(m, str) and m == 'close':210close_requested = True211continue212if m.compass_id not in mavlink_msgs:213# Keep the last two messages so that we get the last progress214# if the last message is the calibration report.215mavlink_msgs[m.compass_id] = [None, m]216else:217l = mavlink_msgs[m.compass_id]218l[0] = l[1]219l[1] = m220221if close_requested:222self._timer.Stop()223self.Destroy()224return225226if not mavlink_msgs:227return228229needs_fit = False230for k in mavlink_msgs:231if k not in self._magcal_panels:232self.add_compass(k)233needs_fit = True234if needs_fit:235self._sizer.Fit(self)236237for k, l in mavlink_msgs.items():238for m in l:239if not m:240continue241panel = self._magcal_panels[k]242if m.get_type() == 'MAG_CAL_PROGRESS':243panel.mavlink_magcal_progress(m)244elif m.get_type() == 'MAG_CAL_REPORT':245panel.mavlink_magcal_report(m)246247248