Compare View

switch
from
...
to
 
Commits (5)
robots/little_john/telemetry/code/monitor/version1/colours.py
... ... @@ -2,4 +2,4 @@ WHITE = (255, 255, 255, 255)
2 2 BLACK = (0, 0, 0, 255)
3 3 RED = (255, 0, 0, 255)
4 4 GREEN = (0, 255, 0, 255)
5   -BLUE = (0, 0, 255, 255)
6 5 \ No newline at end of file
  6 +BLUE = (0, 0, 255, 255)
... ...
robots/little_john/telemetry/code/monitor/version1/getdata.py 0 → 100644
... ... @@ -0,0 +1,46 @@
  1 +# Functions to process data coming in from a serial data stream
  2 +# Written as a telemetry tool by:
  3 +# The UoN Robot Wars Project, 2018
  4 +
  5 +import logging
  6 +import os
  7 +import time
  8 +import serial
  9 +
  10 +
  11 +def getData(starttime, datafeed, testseries, testseries2, fake=False):
  12 + """Check serial port for incoming data"""
  13 + # Note: 'elapsed' is time since last call of this function
  14 + # This works, but it might be better for performance to have two
  15 + # seperate functions, only one of which is run.
  16 + if fake:
  17 + timefromstart = (time.time()-starttime)
  18 + values = [timefromstart, 100*math.sin(timefromstart)]
  19 + # logging.info("Generated test data: " + str(values))
  20 + testseries.addpoint(values)
  21 + testseries2.addpoint(values)
  22 + else:
  23 + try:
  24 + incoming = datafeed.readline()
  25 + except:
  26 + logging.error("Failed to read input data")
  27 +
  28 + try:
  29 + if os == 'Windows':
  30 + values = incoming.strip().split(b", ")
  31 + else:
  32 + values = incoming.strip()
  33 + values = values.split(b', ')
  34 + except:
  35 + logging.error("Failed to parse input data")
  36 + return
  37 +
  38 + try:
  39 + for n, value in enumerate(values):
  40 + values[n] = float(value)
  41 + except:
  42 + logging.error("Failed to convert input to float")
  43 + return
  44 + # logging.info("Recieved data: " + str(values))
  45 + testseries.addpoint([time.time()-starttime] + values)
  46 + testseries2.addpoint(values)
... ...
robots/little_john/telemetry/code/monitor/version1/main.py
... ... @@ -9,7 +9,6 @@
9 9  
10 10 import math
11 11 import time
12   -#import serial
13 12  
14 13 import pyglet
15 14 import numpy
... ... @@ -23,6 +22,7 @@ from serialselect import selectserial
23 22 from colours import *
24 23 from series import Series
25 24 from plot import Plot
  25 +from getdata import getData
26 26  
27 27 logging.basicConfig(format='%(levelname)s:\t%(message)s', level=logging.DEBUG)
28 28 logging.info("Logging system active")
... ... @@ -30,42 +30,40 @@ starttime = time.time()
30 30  
31 31 datafeed = selectserial()
32 32  
33   -if datafeed == None:
  33 +if datafeed is None:
34 34 logging.critical("Failed to open serial port")
35 35 sys.exit()
36   -
37   -if platform.system()=='Windows':
38   - os='Windows'
  36 +
  37 +if platform.system() == 'Windows':
  38 + os = 'Windows'
39 39 else:
40   - os='Other'
  40 + os = 'Other'
41 41 logging.info('OS = ' + os)
42 42  
43   -testseries = Series(points=150, title="Sine wave demo", xname="Time (s)", yname="100sin(t)")
  43 +
  44 +testseries = Series(points=150, xauto=True, ylimits=(0, 1024),
  45 + title="Data from serial (time)",
  46 + xname="Time (s)", yname="ADC output")
  47 +testseries2 = Series(points=150, xlimits=(0, 1024), ylimits=(0, 1024),
  48 + title="Data from serial (xy)",
  49 + xname="ADC0 output", yname="ADC7 output")
  50 +
44 51 plots = []
45 52 plots.append(Plot(testseries))
  53 +plots.append(Plot(testseries2))
  54 +
  55 +
  56 +def shrubbery(ni):
  57 + """ Yes I know these are absurd placeholder names. I'm leaving it like
  58 + that for the moment because I'm not sure if this is really the only
  59 + (or best) way to achive the desired result. clock.schedule_interval takes
  60 + a function, not the result of the function as its argument, so how can
  61 + you pass arguments to it? """
  62 + getData(starttime, datafeed, testseries, testseries2)
  63 +
46 64  
47   -def pollSerial(elapsed):
48   - """Check serial port for incoming data"""
49   - # Note: 'elapsed' is time since last call of this function
50   - # This works, but it might be better for performance to have two seperate functions, only one of which is run.
51   - if os=='Windows':
52   - values = datafeed.readline().strip().split(b", ")
53   - else:
54   - values = datafeed.readline().strip()
55   - values = values.split(b', ')
56   - for n, value in enumerate(values):
57   - values[n] = float(value)
58   - #logging.info("Recieved data: " + str(values))
59   - testseries.addpoint(values)
60   -
61   -def fakePollSerial(elapsed):
62   - """This function immitates the behaviour of pollSerial, for testing purposes"""
63   - timefromstart = (time.time()-starttime)
64   - values = [timefromstart, 100*math.sin(timefromstart)]
65   - #logging.info("Generated test data: " + str(values))
66   - testseries.addpoint(values)
67   -
68   -# Pyglet looks after the main event loop, but this ensures that data keeps being read in
69   -pyglet.clock.schedule_interval(fakePollSerial, 0.04)
  65 +# Pyglet looks after the main event loop,
  66 +# but this ensures that data keeps being read
  67 +pyglet.clock.schedule_interval(shrubbery, 0.01)
70 68  
71 69 pyglet.app.run()
... ...
robots/little_john/telemetry/code/monitor/version1/plot.py
... ... @@ -7,31 +7,38 @@ import numpy
7 7 import logging
8 8 from colours import *
9 9  
  10 +
10 11 class Plot(pyglet.window.Window):
11 12 def __init__(self, series):
12 13 """Setup a the details of a plot, and create a corresponding window"""
13 14 pyglet.window.Window.__init__(self, resizable=True)
14 15 self.set_icon(pyglet.image.load('32x32.png'))
15   - self.set_minimum_size(320,320)
  16 + self.set_minimum_size(320, 320)
16 17 self.series = series
17 18 self.font = 'Arkhip'
18   - self.margins = (0.09, 0.08) # Fractions of window size
  19 + self.margins = (0.09, 0.08) # Fractions of window size
19 20 self.lines = (10, 8)
20   - #self.resizable = True
  21 + # self.resizable = True
21 22 self.set_caption(self.series.title)
22 23  
23 24 def on_resize(self, width, height):
24 25 """Handle a resize event from the pyglet event loop"""
25 26 try:
26   - self.bounds = ((int(self.width * self.margins[0]), int(self.width * (1 - self.margins[0]))),
27   - (int(self.height * self.margins[1]), int(self.height * (1 - self.margins[1]))))
  27 + self.bounds = ((int(self.width * self.margins[0]),
  28 + int(self.width * (1 - self.margins[0]))),
  29 + (int(self.height * self.margins[1]),
  30 + int(self.height * (1 - self.margins[1]))))
28 31 except Exception as e:
29 32 logging.critical(str(e))
30 33 self.close()
31 34 logging.critical('Instance closed')
32 35 sys.exit()
33   - self.tag_size = min(self.height*self.margins[1]*0.3,self.width*self.margins[0]*0.3)
34   - # This sometimes seems to throw an error ('AttributeError: 'Plot' object has no attribute 'margins') when started for a second time from the same instance. Interesting. Causes the plot windows to freeze
  36 + self.tag_size = min(self.height*self.margins[1]*0.3,
  37 + self.width*self.margins[0]*0.3)
  38 + # This sometimes seems to throw an error
  39 + # ('AttributeError: 'Plot' object has no attribute 'margins')
  40 + # when started for a second time from the same instance.
  41 + # Interesting. Causes the plot windows to freeze
35 42 pyglet.window.Window.on_resize(self, width, height)
36 43  
37 44 def on_draw(self):
... ... @@ -40,46 +47,60 @@ class Plot(pyglet.window.Window):
40 47 self.drawHeading()
41 48 self.drawAxis(0)
42 49 self.drawAxis(1)
43   - self.drawLine(self.series)
  50 + self.drawLines(self.series)
44 51  
45 52 def drawBackground(self):
46 53 """Draw the graph background, currently a plain colour"""
47   - pyglet.image.SolidColorImagePattern(WHITE).create_image(self.width, self.height).blit(0, 0)
  54 + #whitepattern = pyglet.image.SolidColorImagePattern(WHITE)
  55 + #whitepattern.create_image(self.width, self.height).blit(0, 0)
  56 + pyglet.image.SolidColorImagePattern(RED).create_image(self.width, self.height).blit(0, 0)
48 57  
49 58 def drawHeading(self):
50   - """Draw a title for the graph (duplicated in the window titlebar, if present"""
  59 + """Draw a title for the graph (duplicated in the window titlebar"""
51 60 heading = pyglet.text.Label(self.series.title, color=BLACK,
52   - font_name=self.font, font_size=self.height*self.margins[0]*0.5,
53   - x=self.width/2, y=self.height-(self.margins[1]),
54   - anchor_x='center', anchor_y='top')
  61 + font_name=self.font,
  62 + font_size=self.height*self.margins[0]*0.5,
  63 + x=self.width/2,
  64 + y=self.height-(self.margins[1]),
  65 + anchor_x='center', anchor_y='top')
55 66 heading.draw()
56 67  
57   - def drawLine(self, series):
58   - xscale = float(self.series.xlimits[1]-self.series.xlimits[0])/(self.bounds[0][1]-self.bounds[0][0])
59   - yscale = float(self.series.ylimits[1]-self.series.ylimits[0])/(self.bounds[1][1]-self.bounds[1][0])
  68 + def drawLines(self, series):
  69 + xscale = float(self.series.xlimits[1]-self.series.xlimits[0] / (self.bounds[0][1]-self.bounds[0][0]))
  70 + yscale = float(self.series.ylimits[1]-self.series.ylimits[0] / (self.bounds[1][1]-self.bounds[1][0]))
  71 +
60 72 lmar = int(self.width * self.margins[0])
61 73 rmar = int(self.width * self.margins[1])
62 74 tmar = int(self.height * self.margins[0])
63 75 bmar = int(self.height * self.margins[1])
64   -
  76 +
65 77 pyglet.gl.glLineWidth(2)
66   -
67   - for n in range(len(series.data) - 1):
68   - x1, y1 = series.data[n][0]-series.xlimits[0], series.data[n][1]-series.ylimits[0]
69   - x2, y2 = series.data[n+1][0]-series.xlimits[0], series.data[n+1][1]-series.ylimits[0]
70   - x1 = int((x1/xscale)+lmar)
71   - y1 = int((y1/yscale)+bmar)
72   - x2 = int((x2/xscale)+lmar)
73   - y2 = int((y2/yscale)+bmar)
74   - pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
75   - ('v2i', (x1, y1, x2, y2)),
76   - ('c3B', (255, 0, 0, 255, 0, 0)))
77   - pyglet.gl.glLineWidth(1)
78 78  
  79 + linecolours = [(255, 0, 0, 255, 0, 0),
  80 + (0, 220, 0, 0, 220, 0),
  81 + (0, 0, 255, 0, 0, 255)]
79 82  
  83 + try:
  84 + for m in range(len(series.data[0])-1):
  85 + for n in range(len(series.data) - 1):
  86 + x1 = series.data[n][0]-series.xlimits[0]
  87 + y1 = series.data[n][m+1]-series.ylimits[0]
  88 + x2 = series.data[n+1][0]-series.xlimits[0]
  89 + y2 = series.data[n+1][m+1]-series.ylimits[0]
  90 + x1 = int((x1/xscale)+lmar)
  91 + y1 = int((y1/yscale)+bmar)
  92 + x2 = int((x2/xscale)+lmar)
  93 + y2 = int((y2/yscale)+bmar)
  94 + pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
  95 + ('v2i', (x1, y1, x2, y2)),
  96 + ('c3B', linecolours[m]))
  97 + except:
  98 + logging.error("Failed to plot lines: corrupt/missing data?")
  99 +
  100 + pyglet.gl.glLineWidth(1)
80 101  
81   - def drawAxis(self, axis): # axis=0 is x, 1 is y
82   - """Draw the gridlines and labels for one axis, specified in the last argument"""
  102 + def drawAxis(self, axis): # axis=0 is x, 1 is y
  103 + """Draw gridlines & labels for one axis, specified as an argument"""
83 104 limita = self.bounds[1-axis][1]
84 105 limitb = self.bounds[1-axis][0]
85 106 start = self.bounds[axis][0]
... ... @@ -88,37 +109,49 @@ class Plot(pyglet.window.Window):
88 109 for pos in numpy.arange(start, stop+1, increment):
89 110 # Using fp arithmetic to avoid intermittent fencepost errors
90 111 pos = int(pos)
91   - if axis==0: # x axis, vertical lines
92   - scale = float(self.series.xlimits[1]-self.series.xlimits[0])/(stop-start)
  112 + if axis == 0: # x axis, vertical lines
  113 + scale = float(self.series.xlimits[1]-self.series.xlimits[0])
  114 + scale /= (stop-start)
93 115 tagvalue = ((pos-start) * scale) + self.series.xlimits[0]
94 116 tagtext = str(int(tagvalue))
95   - pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (pos, limita, pos, limitb)),
96   - ('c3B', (0, 0, 0, 0, 0, 0)))
  117 + pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
  118 + ('v2i', (pos, limita, pos, limitb)),
  119 + ('c3B', (0, 0, 0, 0, 0, 0)))
97 120 tag = pyglet.text.Label(tagtext, color=BLACK,
98   - font_name=self.font, font_size=self.tag_size,
  121 + font_name=self.font,
  122 + font_size=self.tag_size,
99 123 x=pos, y=self.height*self.margins[1],
100 124 anchor_x='left', anchor_y='top')
101 125 axistitle = pyglet.text.Label(self.series.xname, color=BLACK,
102   - font_name=self.font, font_size=self.tag_size,
103   - x=self.width/2, y=0,
104   - anchor_x='center', anchor_y='bottom')
  126 + font_name=self.font,
  127 + font_size=self.tag_size,
  128 + x=self.width/2, y=0,
  129 + anchor_x='center',
  130 + anchor_y='bottom')
105 131 axistitle.draw()
106   - if axis==1: # y axis, horizontal lines
107   - scale = float(self.series.ylimits[1]-self.series.ylimits[0])/(stop-start)
  132 + if axis == 1: # y axis, horizontal lines
  133 + scale = float(self.series.ylimits[1]-self.series.ylimits[0])
  134 + scale /= (stop-start)
108 135 tagvalue = ((pos-start) * scale) + self.series.ylimits[0]
109 136 tagtext = str(int(tagvalue))
110   - pyglet.graphics.draw(2, pyglet.gl.GL_LINES, ('v2i', (limita, pos, limitb, pos)),
111   - ('c3B', (0, 0, 0, 0, 0, 0)))
  137 + pyglet.graphics.draw(2, pyglet.gl.GL_LINES,
  138 + ('v2i', (limita, pos, limitb, pos)),
  139 + ('c3B', (0, 0, 0, 0, 0, 0)))
112 140 tag = pyglet.text.Label(tagtext, color=BLACK,
113   - font_name=self.font, font_size=self.tag_size,
114   - x=self.width*self.margins[0]*0.9, y=pos,
  141 + font_name=self.font,
  142 + font_size=self.tag_size,
  143 + x=self.width*self.margins[0]*0.9,
  144 + y=pos,
115 145 anchor_x='right', anchor_y='center')
116 146 axistitle = pyglet.text.Label(self.series.yname, color=BLACK,
117   - font_name=self.font, font_size=self.tag_size,
118   - x=0, y=self.height/2,
119   - anchor_x='center', anchor_y='top')
120   - pyglet.gl.glPushMatrix() # Set up a new context to avoid confusing the main one
121   - # Tranformation to rotate label, and ensure it ends up in the right place
  147 + font_name=self.font,
  148 + font_size=self.tag_size,
  149 + x=0, y=self.height/2,
  150 + anchor_x='center',
  151 + anchor_y='top')
  152 + # Set up a new context to avoid confusing the main one
  153 + pyglet.gl.glPushMatrix()
  154 + # Tranformation to move label to the right place
122 155 pyglet.gl.glTranslatef(self.height//2, self.height//2, 0.0)
123 156 pyglet.gl.glRotatef(90.0, 0.0, 0.0, 1.0)
124 157 # Draw the axis title using the rotated coordinate system
... ...
robots/little_john/telemetry/code/monitor/version1/serialselect.py
... ... @@ -2,27 +2,29 @@
2 2 # Written as a telemetry tool by:
3 3 # The UoN Robot Wars Project, 2018
4 4  
  5 +
5 6 def selectserial():
6   - """Cross-platform function to find appropriate serial ports, query the user if necessary, and open one of them"""
  7 + """Cross-platform function to find appropriate serial ports, query the
  8 + user if necessary, and open one of them"""
7 9 import platform
8 10 import serial
9 11 import os
10 12 import easygui
11 13 import logging
12   -
  14 +
13 15 devpatterns = ['ttyACM', 'ttyUSB', 'rfcomm']
14 16 targetdevs = []
15   - if platform.system()=='Windows':
  17 + if platform.system() == 'Windows':
16 18 com_ports = ['COM%s' % (i + 1) for i in range(256)]
17 19 for port in com_ports:
18 20 try:
19 21 s = serial.Serial(port)
20 22 s.close()
21 23 targetdevs.append(port)
22   - # Temporarily broadened exception in attempt to make this work on uni PCs
23   - except: # (OSError, serial.SerialException):
  24 + # Temporarily broadened exception to make this work on uni PCs
  25 + except: # (OSError, serial.SerialException):
24 26 pass
25   - os='Windows' #may be useful
  27 + os = 'Windows' # may be useful
26 28 else:
27 29 alldevs = os.listdir("/dev/")
28 30 targetdevs = []
... ... @@ -30,7 +32,7 @@ def selectserial():
30 32 for pattern in devpatterns:
31 33 if pattern in dev:
32 34 targetdevs.append("/dev/" + dev)
33   - os='Other' #may be useful
  35 + os = 'Other' # may be useful
34 36  
35 37 if len(targetdevs) == 0:
36 38 logging.info("No serial device found.")
... ... @@ -42,22 +44,22 @@ def selectserial():
42 44 message = "Please choose a serial port to recieve data through:"
43 45 title = "Found multiple serial ports!"
44 46 serialport = easygui.choicebox(message, title, targetdevs)
45   - if serialport == None:
  47 + if serialport is None:
46 48 logging.info("User cancelled selection dialogue")
47 49 return None
48 50 else:
49 51 logging.info("Only found one likely serial device: " + targetdevs[0])
50 52 serialport = targetdevs[0]
51   -
  53 +
52 54 try:
53 55 datafeed = serial.Serial(
54   - port=serialport,
55   - baudrate = 9600,
56   - parity=serial.PARITY_NONE,
57   - stopbits=serial.STOPBITS_ONE,
58   - bytesize=serial.EIGHTBITS,
59   - timeout=1
60   - )
  56 + port=serialport,
  57 + baudrate=9600,
  58 + parity=serial.PARITY_NONE,
  59 + stopbits=serial.STOPBITS_ONE,
  60 + bytesize=serial.EIGHTBITS,
  61 + timeout=1
  62 + )
61 63  
62 64 logging.info("Sucessfully opened " + serialport + " as data source!")
63 65 return datafeed
... ...
robots/little_john/telemetry/code/monitor/version1/series.py
... ... @@ -4,8 +4,12 @@
4 4  
5 5 import logging
6 6  
  7 +
7 8 class Series:
8   - def __init__(self, points=100, title="Series title", xname="x-axis name", yname="y-axis name"):
  9 + def __init__(self, points=100, xlimits=(0, 100), ylimits=(0, 100),
  10 + xauto=False, yauto=False,
  11 + title="Series title",
  12 + xname="x-axis name", yname="y-axis name"):
9 13 """Set up an object to store a 2D data series"""
10 14 # Proposal:
11 15 # In order to neatly handle multiple lines on the same graph
... ... @@ -20,28 +24,36 @@ class Series:
20 24 self.title = title
21 25 self.xname = xname
22 26 self.yname = yname
23   - self.xlimits = (0, 100)
24   - self.ylimits = (-100, 100)
  27 + self.xlimits = xlimits
  28 + self.ylimits = ylimits
25 29 self.data = []
26 30 self.points = points
27   -
  31 + self.xauto = xauto
  32 + self.yauto = yauto
  33 +
28 34 logging.info("Created series: " + title)
29   -
  35 +
30 36 def addpoint(self, point):
31 37 """Add a point to the dataset, and remove the oldest, if necessary"""
32 38 self.data.append(point)
33 39 if len(self.data) > self.points:
34 40 del self.data[0]
35   - self.autoscale(0)
36   -
37   - def autoscale(self, axis): # axis=0 is x, 1 is y
  41 + try:
  42 + if self.xauto:
  43 + self.autoscale(0)
  44 + if self.yauto:
  45 + self.autoscale(1)
  46 + except:
  47 + logging.error("Series autoscale failed")
  48 +
  49 + def autoscale(self, axis): # axis=0 is x, 1 is y
38 50 minval = self.data[0][axis]
39 51 maxval = self.data[0][axis]
40 52 for value in self.data:
41 53 if value[axis] < minval:
42 54 minval = value[axis]
43 55 if value[axis] > maxval:
44   - maxval = value[axis]
  56 + maxval = value[axis]
45 57 if axis == 0:
46 58 self.xlimits = (minval, maxval)
47 59 else:
... ...
robots/little_john/telemetry/code/robot/analogread_demo/analogread_demo.ino
... ... @@ -6,7 +6,6 @@
6 6 * This code is under the GPL
7 7 */
8 8  
9   -#include <math.h>
10 9 #include <SoftwareSerial.h>
11 10  
12 11 SoftwareSerial Bluetooth(3, 2); // Rx, Tx
... ... @@ -14,7 +13,6 @@ SoftwareSerial Bluetooth(3, 2); // Rx, Tx
14 13 void setup() {
15 14 Serial.begin(9600);
16 15 Bluetooth.begin(115200);
17   - //Bluetooth.print("Hello, wireless world!");
18 16 pinMode(13, OUTPUT);
19 17 }
20 18  
... ... @@ -25,5 +23,5 @@ void loop() {
25 23 Bluetooth.print(a0value);
26 24 Bluetooth.print(", ");
27 25 Bluetooth.println(a7value);
28   - delay(500);
  26 + delay(180);
29 27 }
... ...