diff --git a/sw/ground_segment/python/real_time_plot/messagepicker.py b/sw/ground_segment/python/real_time_plot/messagepicker.py index 33e863cab7..ff33b7365c 100755 --- a/sw/ground_segment/python/real_time_plot/messagepicker.py +++ b/sw/ground_segment/python/real_time_plot/messagepicker.py @@ -32,7 +32,7 @@ class Aircraft(object): class MessagePicker(wx.Frame): - def __init__(self, parent, callback, initIvy = True): + def __init__(self, parent, callback, initIvy=True): wx.Frame.__init__(self, parent, name="MessagePicker", title=u'Message Picker', size=wx.Size(320,640)) self.aircrafts = {} @@ -42,7 +42,7 @@ class MessagePicker(wx.Frame): self.root = self.tree.AddRoot("Telemetry") self.tree.Bind(wx.EVT_LEFT_DCLICK, self.OnDoubleClick) self.tree.Bind(wx.EVT_CHAR, self.OnKeyChar) - self.Bind( wx.EVT_CLOSE, self.OnClose) + self.Bind(wx.EVT_CLOSE, self.OnClose) self.message_interface = IvyMessagesInterface(self.msg_recv, initIvy) def OnClose(self, event): diff --git a/sw/ground_segment/python/real_time_plot/plotpanel.py b/sw/ground_segment/python/real_time_plot/plotpanel.py index a6eb9d8bfc..33ae73f99f 100644 --- a/sw/ground_segment/python/real_time_plot/plotpanel.py +++ b/sw/ground_segment/python/real_time_plot/plotpanel.py @@ -8,15 +8,19 @@ from textdroptarget import * import math import random import sys -import os +from os import getenv, path import messagepicker -sys.path.append(os.getenv("PAPARAZZI_SRC") + "/sw/lib/python") +# if PAPARAZZI_SRC not set, then assume the tree containing this +# file is a reasonable substitute +PPRZ_SRC = getenv("PAPARAZZI_SRC", path.normpath(path.join(path.dirname(path.abspath(__file__)), '../../../../'))) +sys.path.append(PPRZ_SRC + "/sw/lib/python") from pprz_msg import messages_xml_map -class plot_data: - def __init__(self, ivy_msg_id, title, width, color = None): + +class PlotData: + def __init__(self, ivy_msg_id, title, width, color=None): self.id = ivy_msg_id self.title = title self.SetPlotSize(width) @@ -30,11 +34,11 @@ class plot_data: self.scale = 1.0 self.offset = 0.0 - if (color != None): + if color is not None: self.color = color else: - r,g,b = random.randint(0,255),random.randint(0,255),random.randint(0,255) - self.color = wx.Color(r,g,b) + r, g, b = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) + self.color = wx.Color(r, g, b) def SetRealTime(self, value): self.real_time = value @@ -47,8 +51,8 @@ class plot_data: def SetPlotSize(self, size): self.size = size - self.index = size-1 # holds the index of the next point to add and the first point to draw - self.data = [] # holds the list of points to plot + self.index = size - 1 # holds the index of the next point to add and the first point to draw + self.data = [] # holds the list of points to plot for i in range(size): self.data.append(None) @@ -58,19 +62,18 @@ class plot_data: def AddPoint(self, point, x_axis): self.data[self.index] = point - if self.real_time or (x_axis != None): - self.index = (self.index + 1) % self.size # increment index to next point + if self.real_time or (x_axis is not None): + self.index = (self.index + 1) % self.size # increment index to next point self.data[self.index] = None - def DrawTitle(self, dc, margin, width, height): - text ='avg:%.2f std:%.2f %s' % (self.avg, self.std_dev, self.title) + text = 'avg:%.2f std:%.2f %s' % (self.avg, self.std_dev, self.title) - (w,h) = dc.GetTextExtent(text) + (w, h) = dc.GetTextExtent(text) dc.SetBrush(wx.Brush(self.color)) - dc.DrawRectangle( width-h-margin, height, h, h) - dc.DrawText(text, width-2*margin-w-h, height) + dc.DrawRectangle(width - h - margin, height, h, h) + dc.DrawText(text, width - 2 * margin - w - h, height) return h def DrawCurve(self, dc, width, height, margin, _max_, _min_, x_axis): @@ -78,20 +81,20 @@ class plot_data: self.SetPlotSize(width) return - if (not self.real_time) and (x_axis == None): - self.index = (self.index + 1) % self.size # increment index to next point + if (not self.real_time) and (x_axis is None): + self.index = (self.index + 1) % self.size # increment index to next point self.data[self.index] = None - if x_axis != None: + if x_axis is not None: (x_min, x_max) = x_axis.GetXMinMax() - dc.SetPen(wx.Pen(self.color,1)) + dc.SetPen(wx.Pen(self.color, 1)) if _max_ < _min_: - (_min_, _max_) = (-1,1) #prevent divide by zero or inversion + (_min_, _max_) = (-1, 1) # prevent divide by zero or inversion if _max_ == _min_: - (_min_, _max_) = (_max_-0.5, _max_+0.5) - delta = _max_-_min_ - dy = (height - margin*2) / delta + (_min_, _max_) = (_max_ - 0.5, _max_ + 0.5) + delta = _max_ - _min_ + dy = (height - margin * 2) / delta n = 0 sums = 0.0 @@ -99,31 +102,31 @@ class plot_data: lines = [] point_1 = None for i in range(self.size): - ix = (i+self.index) % self.size + ix = (i + self.index) % self.size point = self.data[ix] - if point == None: + if point is None: continue n += 1 sums = sums + point - sum_squares = sum_squares + (point*point) + sum_squares = sum_squares + (point * point) - if x_axis != None: + if x_axis is not None: x = x_axis.data[ix] - if x == None: + if x is None: continue - dx = (width-1) / (x_max-x_min) - x = int((x-x_min) * dx) + dx = (width - 1) / (x_max - x_min) + x = int((x - x_min) * dx) else: x = i * width / self.size scaled_point = (point + self.offset) * self.scale - y = height - margin - int((scaled_point - _min_)*dy) + y = height - margin - int((scaled_point - _min_) * dy) - if point_1 != None: + if point_1 is not None: line = (point_1[0], point_1[1], x, y) - lines.append( line) - point_1 = (x,y) + lines.append(line) + point_1 = (x, y) dc.DrawLineList(lines) if n > 0: @@ -136,41 +139,43 @@ class plot_data: for i in range(self.size): point = self.data[i] - if point == None: continue - - x_min = min( x_min, point) - x_max = max( x_max, point) + if point is None: + continue + x_min = min(x_min, point) + x_max = max(x_max, point) if x_max < x_min: - (x_min, x_max) = (-1,1) #prevent divide by zero or inversion + (x_min, x_max) = (-1, 1) # prevent divide by zero or inversion if x_max == x_min: - (x_min, x_max) = (x_max-0.5, x_max+0.5) + (x_min, x_max) = (x_max - 0.5, x_max + 0.5) self.x_max = x_max self.x_min = x_min return (x_min, x_max) -_IVY_APPNAME='JobyPlot' +_IVY_APPNAME = 'RealtimePlot' _IVY_STRING = '(%s %s .*$)' -#_IVY_STRING = '^([^ ]*) +(%s( .*|$))' ## <-- from original ocaml (doesn't work here, just returns Sender field...) + + +# _IVY_STRING = '^([^ ]*) +(%s( .*|$))' ## <-- from original ocaml (doesn't work here, just returns Sender field...) def create(parent, frame): return PlotPanel(parent, frame) -class PlotPanel(): +class PlotPanel(object): def __init__(self, parent, frame): - self.parent = parent # we are drawing on our parent, so dc comes from this - self.frame = frame # the frame owns any controls we might need to update + self.parent = parent # we are drawing on our parent, so dc comes from this + self.frame = frame # the frame owns any controls we might need to update - parent.SetDropTarget( TextDropTarget(self)) # calls self.OnDropText when drag and drop complete + parent.SetDropTarget(TextDropTarget(self)) # calls self.OnDropText when drag and drop complete self.InitIvy() self.width = 800 self.height = 200 self.margin = min(self.height / 10, 20) - self.font = wx.Font(self.margin/2, wx.DEFAULT, wx.FONTFLAG_DEFAULT, wx.FONTWEIGHT_NORMAL) + self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTFLAG_DEFAULT, wx.FONTWEIGHT_NORMAL) self.pixmap = wx.EmptyBitmap(self.width, self.height) self.plot_size = self.width self.max = -1e32 @@ -185,12 +190,12 @@ class PlotPanel(): messages_xml_map.parse_messages() # start the timer - self.timer = wx.FutureCall( self.plot_interval, self.OnTimer) + self.timer = wx.FutureCall(self.plot_interval, self.OnTimer) def SetPlotInterval(self, value): self.plot_interval = value self.timer.Restart(self.plot_interval) - self.timer = wx.FutureCall( self.plot_interval, self.OnTimer) + self.timer = wx.FutureCall(self.plot_interval, self.OnTimer) def SetAutoScale(self, value): self.auto_scale = value @@ -205,7 +210,7 @@ class PlotPanel(): if pause: self.timer.Stop() else: - self.timer = wx.FutureCall( self.plot_interval, self.OnTimer) + self.timer = wx.FutureCall(self.plot_interval, self.OnTimer) def ResetScale(self): self.max = -1e32 @@ -227,11 +232,11 @@ class PlotPanel(): def InitIvy(self): # initialising the bus - IvyInit(_IVY_APPNAME, # application name for Ivy - "",#"[%s is ready]" % IVYAPPNAME, # ready message - 0, # main loop is local (ie. using IvyMainloop) - lambda x,y: y, # handler called on connection/deconnection - lambda x,y: y # handler called when a diemessage is received + IvyInit(_IVY_APPNAME, # application name for Ivy + "", # "[%s is ready]" % IVYAPPNAME, # ready message + 0, # main loop is local (ie. using IvyMainloop) + lambda x, y: y, # handler called on connection/deconnection + lambda x, y: y # handler called when a diemessage is received ) # starting the bus @@ -241,7 +246,7 @@ class PlotPanel(): logging.getLogger('Ivy').setLevel(logging.WARN) IvyStart("") # binding to every message - #IvyBindMsg(self.OnIvyMsg, "(.*)") + # IvyBindMsg(self.OnIvyMsg, "(.*)") except: IvyStop() @@ -250,7 +255,7 @@ class PlotPanel(): self.BindCurve(int(ac_id), message, field) def OnIvyMsg(self, agent, *larg): - #print(larg[0]) + # print(larg[0]) data = larg[0].split(' ') ac_id = int(data[0]) message = data[1] @@ -264,22 +269,22 @@ class PlotPanel(): for field in self.plots[ac_id][message]: plot = self.plots[ac_id][message][field] ix = messages_xml_map.message_dictionary["telemetry"][message].index(field) - point = float(data[ix+2]) + point = float(data[ix + 2]) - if self.x_axis == None or self.x_axis.id != plot.id: + if self.x_axis is None or self.x_axis.id != plot.id: if self.auto_scale: scaled_point = (point + plot.offset) * plot.scale - self.max = max( self.max, scaled_point) - self.min = min( self.min, scaled_point) + self.max = max(self.max, scaled_point) + self.min = min(self.min, scaled_point) - if self.x_axis != None: + if self.x_axis is not None: plot.index = self.x_axis.index plot.AddPoint(point, self.x_axis) - def BindCurve(self, ac_id, message, field, color = None, use_as_x = False): + def BindCurve(self, ac_id, message, field, color=None, use_as_x=False): # -- add this telemetry to our list of things to plot ... message_string = _IVY_STRING % (ac_id, message) - #print('Binding to %s' % message_string) + # print('Binding to %s' % message_string) if ac_id not in self.plots: self.plots[ac_id] = {} @@ -288,14 +293,15 @@ class PlotPanel(): self.plots[ac_id][message] = {} if field in self.plots[ac_id][message]: - self.plots[ac_id][message][field].color = wx.Color(random.randint(0,255),random.randint(0,255),random.randint(0,255)) + self.plots[ac_id][message][field].color = wx.Color(random.randint(0, 255), random.randint(0, 255), + random.randint(0, 255)) return ivy_id = IvyBindMsg(self.OnIvyMsg, str(message_string)) - title = '%i:%s:%s' % (ac_id, message, field) - self.plots[ac_id][message][field] = plot_data( ivy_id, title, self.plot_size, color) - self.frame.AddCurve( ivy_id, title, use_as_x) - if (use_as_x): + title = '%i:%s:%s' % (ac_id, message, field) + self.plots[ac_id][message][field] = PlotData(ivy_id, title, self.plot_size, color) + self.frame.AddCurve(ivy_id, title, use_as_x) + if use_as_x: self.x_axis = self.plots[ac_id][message][field] def CalcMinMax(self, plot): @@ -314,62 +320,62 @@ class PlotPanel(): return (None, None, None) def FindPlot(self, ivy_id): - (ac_id, msg, field) = self.FindPlotName( ivy_id) - if (ac_id == None): + (ac_id, msg, field) = self.FindPlotName(ivy_id) + if ac_id is None: return None return self.plots[ac_id][msg][field] def RemovePlot(self, ivy_id): - (ac_id, msg, field) = self.FindPlotName( ivy_id) - if ac_id == None: - return + (ac_id, msg, field) = self.FindPlotName(ivy_id) + if ac_id is None: + return - if (self.x_axis != None) and (self.x_axis.id == ivy_id): - self.x_axis = None + if (self.x_axis is not None) and (self.x_axis.id == ivy_id): + self.x_axis = None - IvyUnBindMsg( ivy_id) - del self.plots[ac_id][msg][field] - if len(self.plots[ac_id][msg]) == 0: - del self.plots[ac_id][msg] + IvyUnBindMsg(ivy_id) + del self.plots[ac_id][msg][field] + if len(self.plots[ac_id][msg]) == 0: + del self.plots[ac_id][msg] def OffsetPlot(self, ivy_id, offset): - plot = self.FindPlot( ivy_id) - if plot == None: - return + plot = self.FindPlot(ivy_id) + if plot is None: + return - plot.SetOffset(offset) - print('panel value: %.2f' % value) - CalcMinMax(plot) + plot.SetOffset(offset) + print('panel value: %.2f' % value) + CalcMinMax(plot) def ScalePlot(self, ivy_id, offset): - plot = self.FindPlot( ivy_id) - if plot == None: - return + plot = self.FindPlot(ivy_id) + if plot is None: + return - plot.SetScale(offset) - CalcMinMax(plot) + plot.SetScale(offset) + CalcMinMax(plot) def SetRealTime(self, ivy_id, value): - plot = self.FindPlot( ivy_id) - if plot == None: - return + plot = self.FindPlot(ivy_id) + if plot is None: + return - plot.SetRealTime(value) + plot.SetRealTime(value) def SetXAxis(self, ivy_id): - plot = self.FindPlot( ivy_id) - if plot == None: - return + plot = self.FindPlot(ivy_id) + if plot is None: + return - self.x_axis = plot + self.x_axis = plot def ClearXAxis(self): - self.x_axis = None + self.x_axis = None def OnSize(self, size): (width, height) = size - if( self.width == width and self.height == height): + if self.width == width and self.height == height: return self.pixmap = wx.EmptyBitmap(width, height) @@ -377,7 +383,7 @@ class PlotPanel(): self.height = height self.plot_size = width self.margin = min(self.height / 10, 20) - self.font = wx.Font(self.margin/2, wx.DEFAULT, wx.FONTFLAG_DEFAULT, wx.FONTWEIGHT_NORMAL) + self.font = wx.Font(self.margin / 2, wx.DEFAULT, wx.FONTFLAG_DEFAULT, wx.FONTWEIGHT_NORMAL) def OnTimer(self): self.timer.Restart(self.plot_interval) @@ -386,8 +392,8 @@ class PlotPanel(): def DrawFrame(self): dc = wx.ClientDC(self.parent) - bdc = wx.BufferedDC( dc, self.pixmap) - bdc.SetBackground( wx.Brush( "White")) + bdc = wx.BufferedDC(dc, self.pixmap) + bdc.SetBackground(wx.Brush("White")) bdc.Clear() self.DrawBackground(bdc, self.width, self.height) @@ -397,61 +403,60 @@ class PlotPanel(): for message in self.plots[ac_id]: for field in self.plots[ac_id][message]: plot = self.plots[ac_id][message][field] - if (self.x_axis != None) and (self.x_axis.id == plot.id): + if (self.x_axis is not None) and (self.x_axis.id == plot.id): continue title_height = plot.DrawTitle(bdc, 2, self.width, title_y) plot.DrawCurve(bdc, self.width, self.height, self.margin, self.max, self.min, self.x_axis) title_y += title_height + 2 - def DrawBackground(self, dc, width, height): # Time Graduations dc.SetFont(self.font) - if self.x_axis == None: + if self.x_axis is None: t = self.plot_interval * width t1 = "0.0s" - t2 = "-%.1fs" % (t/2000.0) - t3 = "-%.1fs" % (t/1000.0) + t2 = "-%.1fs" % (t / 2000.0) + t3 = "-%.1fs" % (t / 1000.0) else: x_max = self.x_axis.x_max x_min = self.x_axis.x_min t1 = "%.2f" % x_max - t2 = "%.2f" % (x_min + (x_max-x_min)/2.0) + t2 = "%.2f" % (x_min + (x_max - x_min) / 2.0) t3 = "%.2f" % x_min - (w,h) = dc.GetTextExtent(t1) - dc.DrawText(t1, width-w, height-h) - #(w,h) = dc.GetTextExtent(t2) #save time since h will be the same - dc.DrawText(t2, width/2, height-h) - #(w,h) = dc.GetTextExtent(t3) #save time since h will be the same - dc.DrawText(t3, 0, height-h) + (w, h) = dc.GetTextExtent(t1) + dc.DrawText(t1, width - w, height - h) + # (w,h) = dc.GetTextExtent(t2) #save time since h will be the same + dc.DrawText(t2, width / 2, height - h) + # (w,h) = dc.GetTextExtent(t3) #save time since h will be the same + dc.DrawText(t3, 0, height - h) # Y graduations if self.max == -1e32: return (_min_, _max_) = (self.min, self.max) - if _max_ < _min_: #prevent divide by zero or inversion + if _max_ < _min_: # prevent divide by zero or inversion (_min_, _max_) = (-1, 1) if _max_ == _min_: - (_min_, _max_) = (_max_-0.5, _max_+0.5) + (_min_, _max_) = (_max_ - 0.5, _max_ + 0.5) - delta = _max_-_min_ - dy = (height - self.margin*2) / delta - scale = math.log10( delta) + delta = _max_ - _min_ + dy = (height - self.margin * 2) / delta + scale = math.log10(delta) d = math.pow(10.0, math.floor(scale)) u = d - if delta < 2*d: - u=d/5 - elif delta < 5*d: - u=d/2 - tick_min =_min_ - math.fmod(_min_, u) - for i in range( int(delta/u) + 1): - tick = tick_min + float(i)*u + if delta < 2 * d: + u = d / 5 + elif delta < 5 * d: + u = d / 2 + tick_min = _min_ - math.fmod(_min_, u) + for i in range(int(delta / u) + 1): + tick = tick_min + float(i) * u s = str(tick) - (w,h) = dc.GetTextExtent(s) - y = height-self.margin-int((tick-_min_)*dy)-h/2 + (w, h) = dc.GetTextExtent(s) + y = height - self.margin - int((tick - _min_) * dy) - h / 2 dc.DrawText(s, 0, y) diff --git a/sw/ground_segment/python/real_time_plot/realtimeplotapp.py b/sw/ground_segment/python/real_time_plot/realtimeplotapp.py index c47d016370..2b4df99aa5 100755 --- a/sw/ground_segment/python/real_time_plot/realtimeplotapp.py +++ b/sw/ground_segment/python/real_time_plot/realtimeplotapp.py @@ -1,30 +1,24 @@ #!/usr/bin/env python -#Boa:App:BoaApp import wx import getopt import sys import plotframe -modules ={u'PlotFrame': [1, 'Main frame of Application', u'plotframe.py'], - u'messages_xml_map': [0, '', u'messages_xml_map.py'], - u'plotpanel': [0, '', u'plotpanel.py'], - u'realtimeplotapp': [0, '', u'realtimeplotapp.py'], - u'textdroptarget': [0, '', u'textdroptarget.py']} class RealTimePlotApp(wx.App): def OnInit(self): self.main = plotframe.create(None) self.main.Show() self.SetTopWindow(self.main) - opts, args = getopt.getopt(sys.argv[1:], "p:", - ["plot"]) - for o,a in opts: + opts, args = getopt.getopt(sys.argv[1:], "p:", ["plot"]) + for o, a in opts: if o in ("-p", "--plot"): [ac_id, message, field, color, use_x] = a.split(':') self.main.AddPlot(int(ac_id), message, field, color, bool(int(use_x))) return True + def main(): application = RealTimePlotApp(0) application.MainLoop()