Blame view

telemetry/code/monitor/graph_plotter_rewrite.py 6.88 KB
001c428d   Christopher Stone   Beginnings of a c...
1
2
#!/usr/bin/env python

dc810420   Christopher Stone   Added comments an...
3
4
5
6
7
8
# Tool to draw live graphs of data coming in from serial port
# Written as a telemetry tool by:
# The UoN Robot Wars Project, 2018

# This code is incomplete, and is missing core features

001c428d   Christopher Stone   Beginnings of a c...
9
10
11
import pyglet
#import math
#import time
e4e75d3f   Christopher Stone   Accept data from ...
12
import serial
3ce449c7   Christopher Stone   Added axis title ...
13
import numpy
001c428d   Christopher Stone   Beginnings of a c...
14
15
16

from colours import *

e4e75d3f   Christopher Stone   Accept data from ...
17
18
19
20
21
22
23
24
25
datafeed = serial.Serial(
port='/dev/ttyUSB0',
baudrate = 9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)

6432a3a5   Christopher Stone   Added class to ke...
26
27
class Series:
    def __init__(self, points=100, title="Series title", xname="x-axis name", yname="y-axis name"):
dc810420   Christopher Stone   Added comments an...
28
        """Set up an object to store a 2D data series"""
d7138d89   Christopher Stone   Added basis for l...
29
30
31
32
33
34
35
36
37
38
        # Proposal:
        #   In order to neatly handle multiple lines on the same graph
        #   a series is not so much a set of 2D data points, but a number
        #   of series sharing an x axis. For example
        #       (time, temperature)
        #       (time, xaccel, yaccel, zaccel)
        #   The latter seta of points much be of like nature and share an axis
        #       (time, temperature, xaccel)
        #   would be a meaningless thing to plot on a single graph anyway
        
6432a3a5   Christopher Stone   Added class to ke...
39
40
41
        self.title = title
        self.xname = xname
        self.yname = yname
8ad3fe93   Christopher Stone   Replaced placehol...
42
43
        self.xlimits = (100, 0)
        self.ylimits = (0, 255)
6432a3a5   Christopher Stone   Added class to ke...
44
45
46
        self.data = []
        self.points = points
    def addpoint(self, point):
dc810420   Christopher Stone   Added comments an...
47
        """Add a point to the dataset, and remove the oldest, if necessary"""
6432a3a5   Christopher Stone   Added class to ke...
48
49
        self.data.append(point)
        if len(self.data) > self.points:
4863d03c   Christopher Stone   Fixed obvious bug...
50
            del self.data[0]
6432a3a5   Christopher Stone   Added class to ke...
51

d9082b1e   Christopher Stone   Made plot a subcl...
52
53
class Plot(pyglet.window.Window):
    def __init__(self, series):
01bfd701   Christopher Stone   Added docstrings
54
        """Setup a the details of a plot, and create a corresponding window"""
d9082b1e   Christopher Stone   Made plot a subcl...
55
        pyglet.window.Window.__init__(self, resizable=True)
6432a3a5   Christopher Stone   Added class to ke...
56
        self.series = series
948da001   Christopher Stone   More progress on ...
57
        self.font = 'Arkhip'
5ee0c27d   Christopher Stone   Cleaned up axis t...
58
        self.margins = (0.09, 0.08) # Fractions of window size
8ad3fe93   Christopher Stone   Replaced placehol...
59
        self.lines = (10, 8)
d9082b1e   Christopher Stone   Made plot a subcl...
60
        #self.resizable = True
3ce449c7   Christopher Stone   Added axis title ...
61
        self.set_caption(self.series.title)
6dbc3a2c   Christopher Stone   fixed line coordi...
62
63
        
    def on_resize(self, width, height):
dc810420   Christopher Stone   Added comments an...
64
        """Handle a resize event from the pyglet event loop"""
d9082b1e   Christopher Stone   Made plot a subcl...
65
        self.bounds = ((int(self.width * self.margins[0]), int(self.width * (1 - self.margins[0]))),
6dbc3a2c   Christopher Stone   fixed line coordi...
66
67
                (int(self.height * self.margins[1]), int(self.height * (1 - self.margins[1]))))
        pyglet.window.Window.on_resize(self, width, height)
948da001   Christopher Stone   More progress on ...
68
        
d9082b1e   Christopher Stone   Made plot a subcl...
69
    def on_draw(self):
01bfd701   Christopher Stone   Added docstrings
70
        """Draw all the components of the graph"""
948da001   Christopher Stone   More progress on ...
71
72
        self.drawBackground()
        self.drawHeading()
4cab8d4c   Christopher Stone   work towards draw...
73
74
        self.drawAxis(0)
        self.drawAxis(1)
948da001   Christopher Stone   More progress on ...
75
        
001c428d   Christopher Stone   Beginnings of a c...
76
    def drawBackground(self):
01bfd701   Christopher Stone   Added docstrings
77
        """Draw the graph background, currently a plain colour"""
d9082b1e   Christopher Stone   Made plot a subcl...
78
        pyglet.image.SolidColorImagePattern(WHITE).create_image(self.width, self.height).blit(0, 0)
001c428d   Christopher Stone   Beginnings of a c...
79
80
        
    def drawHeading(self):
01bfd701   Christopher Stone   Added docstrings
81
        """Draw a title for the graph (duplicated in the window titlebar, if present"""
3ce449c7   Christopher Stone   Added axis title ...
82
        heading = pyglet.text.Label(self.series.title, color=BLACK,
a1f7d747   Christopher Stone   Improvements to l...
83
84
                            font_name=self.font, font_size=self.height*self.margins[0]*0.5,
                            x=self.width/2, y=self.height-(self.margins[1]),
001c428d   Christopher Stone   Beginnings of a c...
85
86
                            anchor_x='center', anchor_y='top')
        heading.draw()
d7138d89   Christopher Stone   Added basis for l...
87
88
        
    def drawLine(self):
112021c4   Christopher Stone   Small addition to...
89
90
        for n in range(len(data) - 1):
            a, b = data[n], data[n+1]
d7138d89   Christopher Stone   Added basis for l...
91
            pass
cad30d6c   Christopher Stone   Progress towards ...
92
     
4cab8d4c   Christopher Stone   work towards draw...
93
    def drawAxis(self, axis): # axis=0 is x, 1 is y
dc810420   Christopher Stone   Added comments an...
94
        """Draw the gridlines and labels for one axis, specified in the last argument"""
a1f7d747   Christopher Stone   Improvements to l...
95
96
97
98
        limita = self.bounds[1-axis][1]
        limitb = self.bounds[1-axis][0]
        start = self.bounds[axis][0]
        stop = self.bounds[axis][1]
4cab8d4c   Christopher Stone   work towards draw...
99
100
        increment = float(stop-start)/self.lines[axis]
        for pos in numpy.arange(start, stop+1, increment):
3ce449c7   Christopher Stone   Added axis title ...
101
            # Using fp arithmetic to avoid intermittent fencepost errors
4cab8d4c   Christopher Stone   work towards draw...
102
            pos = int(pos)
a1f7d747   Christopher Stone   Improvements to l...
103
            if axis==0:   # x axis, vertical lines
8ad3fe93   Christopher Stone   Replaced placehol...
104
105
                scale = float(self.series.xlimits[1]-self.series.xlimits[0])/(stop-start)
                tagvalue = ((pos-start) * scale) + self.series.xlimits[0]
5ee0c27d   Christopher Stone   Cleaned up axis t...
106
                tagtext = str(int(tagvalue))
a1f7d747   Christopher Stone   Improvements to l...
107
                pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (pos, limita, pos, limitb)),
3ce449c7   Christopher Stone   Added axis title ...
108
                                 ('c3B', (0, 0, 0, 0, 0, 0)))
8ad3fe93   Christopher Stone   Replaced placehol...
109
                tag = pyglet.text.Label(tagtext, color=BLACK,
5ee0c27d   Christopher Stone   Cleaned up axis t...
110
111
                                        font_name=self.font, font_size=self.height*self.margins[1]*0.3,
                                        x=pos, y=self.height*self.margins[1],
4cab8d4c   Christopher Stone   work towards draw...
112
                                        anchor_x='left', anchor_y='top')
a1f7d747   Christopher Stone   Improvements to l...
113
                axistitle = pyglet.text.Label(self.series.xname, color=BLACK,
5ee0c27d   Christopher Stone   Cleaned up axis t...
114
                                                font_name=self.font, font_size=self.height*self.margins[1]*0.3,
a1f7d747   Christopher Stone   Improvements to l...
115
116
                                                x=self.width/2, y=0,
                                                anchor_x='center', anchor_y='bottom')
d5bc030b   Christopher Stone   Rotated y-axis ti...
117
                axistitle.draw()
a1f7d747   Christopher Stone   Improvements to l...
118
            if axis==1:  # y axis, horizontal lines
8ad3fe93   Christopher Stone   Replaced placehol...
119
120
                scale = float(self.series.ylimits[1]-self.series.ylimits[0])/(stop-start)
                tagvalue = ((pos-start) * scale) + self.series.ylimits[0]
5ee0c27d   Christopher Stone   Cleaned up axis t...
121
                tagtext = str(int(tagvalue))
a1f7d747   Christopher Stone   Improvements to l...
122
                pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (limita, pos, limitb, pos)),
4cab8d4c   Christopher Stone   work towards draw...
123
                                 ('c3B', (0, 0, 0, 0, 0, 0)))
8ad3fe93   Christopher Stone   Replaced placehol...
124
                tag = pyglet.text.Label(tagtext, color=BLACK,
5ee0c27d   Christopher Stone   Cleaned up axis t...
125
126
                                        font_name=self.font, font_size=self.width*self.margins[0]*0.2,
                                        x=self.width*self.margins[0]*0.9, y=pos,
4cab8d4c   Christopher Stone   work towards draw...
127
                                        anchor_x='right', anchor_y='center')
a1f7d747   Christopher Stone   Improvements to l...
128
                axistitle = pyglet.text.Label(self.series.yname, color=BLACK,
5ee0c27d   Christopher Stone   Cleaned up axis t...
129
                                                font_name=self.font, font_size=self.height*self.margins[0]*0.3,
a1f7d747   Christopher Stone   Improvements to l...
130
                                                x=0, y=self.height/2,
d5bc030b   Christopher Stone   Rotated y-axis ti...
131
                                                anchor_x='center', anchor_y='top')
dc810420   Christopher Stone   Added comments an...
132
133
                pyglet.gl.glPushMatrix()   # Set up a new context to avoid confusing the main one
                # Tranformation to rotate label, and ensure it ends up in the right place
d5bc030b   Christopher Stone   Rotated y-axis ti...
134
                pyglet.gl.glTranslatef(self.height//2, self.height//2, 0.0)
dc810420   Christopher Stone   Added comments an...
135
136
                pyglet.gl.glRotatef(90.0, 0.0, 0.0, 1.0)
                # Draw the axis title using the rotated coordinate system
d5bc030b   Christopher Stone   Rotated y-axis ti...
137
                axistitle.draw()
dc810420   Christopher Stone   Added comments an...
138
                # Return everything to its previous state
d5bc030b   Christopher Stone   Rotated y-axis ti...
139
                pyglet.gl.glPopMatrix()
d5bc030b   Christopher Stone   Rotated y-axis ti...
140

3ce449c7   Christopher Stone   Added axis title ...
141
            tag.draw()
a1f7d747   Christopher Stone   Improvements to l...
142
        
6432a3a5   Christopher Stone   Added class to ke...
143
144
testseries = Series()

001c428d   Christopher Stone   Beginnings of a c...
145
plots = []         
6432a3a5   Christopher Stone   Added class to ke...
146
plots.append(Plot(testseries))
001c428d   Christopher Stone   Beginnings of a c...
147

9159742e   Christopher Stone   Found out what th...
148
def pollSerial(elapsed):
e4e75d3f   Christopher Stone   Accept data from ...
149
    """Check serial port for incoming data"""
dc810420   Christopher Stone   Added comments an...
150
    # Note: 'elapsed' is time since last call of this function
e4e75d3f   Christopher Stone   Accept data from ...
151
152
    values = datafeed.readline().strip().split(", ")
    testseries.addpoint(values)
948da001   Christopher Stone   More progress on ...
153

dc810420   Christopher Stone   Added comments an...
154
# Pyglet looks after the main event loop, but this ensures that data keeps being read in
948da001   Christopher Stone   More progress on ...
155
pyglet.clock.schedule_interval(pollSerial, 0.1)
001c428d   Christopher Stone   Beginnings of a c...
156
157

pyglet.app.run()