r4647 - in developers/werner/ahrt/host/tmc: . demo lib
werner at docs.openmoko.org
werner at docs.openmoko.org
Tue Sep 16 02:45:40 CEST 2008
Author: werner
Date: 2008-09-16 02:45:40 +0200 (Tue, 16 Sep 2008)
New Revision: 4647
Added:
developers/werner/ahrt/host/tmc/demo/dxplore.py
developers/werner/ahrt/host/tmc/lib/dxplore.py
Removed:
developers/werner/ahrt/host/tmc/demo/dplore.py
developers/werner/ahrt/host/tmc/lib/dplore.py
Modified:
developers/werner/ahrt/host/tmc/setup.py
Log:
Renamed dplore.py to the less pessimistic dxplore.py
Sorry for the monster commit ...
Deleted: developers/werner/ahrt/host/tmc/demo/dplore.py
===================================================================
--- developers/werner/ahrt/host/tmc/demo/dplore.py 2008-09-16 00:11:17 UTC (rev 4646)
+++ developers/werner/ahrt/host/tmc/demo/dplore.py 2008-09-16 00:45:40 UTC (rev 4647)
@@ -1,47 +0,0 @@
-#!/usr/bin/python
-
-#
-# WORK IN PROGRESS !
-#
-# TODO:
-#
-# - just taking the minimum of all sample steps does not guarantee that the
-# true sample rate is used (e.g., two symmetrical square waves with a period
-# of 4 and a phase offset of 1 each have a sample step of 2. This could be
-# solved by using the same zero reference for all waves.
-#
-# - make sure all waves start at the same time
-#
-# - pad or truncate all waves to the same length
-#
-# - all the conditioning and conversion into bit arrays is complex enough to
-# justify putting it into the library
-#
-
-from tmc.dplore import dplore
-from tmc.wave import waves
-from sys import argv
-
-
-w = waves()
-w.load(argv[1])
-
-dig = []
-for wv in w:
- dig.append(wv.digitize(0.5))
-
-min = dig[1].sample_step()
-for d in dig[2:]:
- step = d.sample_step()
- if step is not None and step < min:
- min = step
-
-da = []
-for d in dig[1:]:
- a = d.sample(min)
-# print len(a)
- if len(a) >= 1500:
- da.append(a)
-
-da.reverse()
-dplore(da, dig[1].start(), min, argv[1])
Copied: developers/werner/ahrt/host/tmc/demo/dxplore.py (from rev 4646, developers/werner/ahrt/host/tmc/demo/dplore.py)
===================================================================
--- developers/werner/ahrt/host/tmc/demo/dxplore.py (rev 0)
+++ developers/werner/ahrt/host/tmc/demo/dxplore.py 2008-09-16 00:45:40 UTC (rev 4647)
@@ -0,0 +1,47 @@
+#!/usr/bin/python
+
+#
+# WORK IN PROGRESS !
+#
+# TODO:
+#
+# - just taking the minimum of all sample steps does not guarantee that the
+# true sample rate is used (e.g., two symmetrical square waves with a period
+# of 4 and a phase offset of 1 each have a sample step of 2. This could be
+# solved by using the same zero reference for all waves.
+#
+# - make sure all waves start at the same time
+#
+# - pad or truncate all waves to the same length
+#
+# - all the conditioning and conversion into bit arrays is complex enough to
+# justify putting it into the library
+#
+
+from tmc.dxplore import dxplore
+from tmc.wave import waves
+from sys import argv
+
+
+w = waves()
+w.load(argv[1])
+
+dig = []
+for wv in w:
+ dig.append(wv.digitize(0.5))
+
+min = dig[1].sample_step()
+for d in dig[2:]:
+ step = d.sample_step()
+ if step is not None and step < min:
+ min = step
+
+da = []
+for d in dig[1:]:
+ a = d.sample(min)
+# print len(a)
+ if len(a) >= 1500:
+ da.append(a)
+
+da.reverse()
+dxplore(da, dig[1].start(), min, argv[1])
Deleted: developers/werner/ahrt/host/tmc/lib/dplore.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/dplore.py 2008-09-16 00:11:17 UTC (rev 4646)
+++ developers/werner/ahrt/host/tmc/lib/dplore.py 2008-09-16 00:45:40 UTC (rev 4647)
@@ -1,584 +0,0 @@
-#!/usr/bin/python
-
-#
-# WORK IN PROGRESS !
-#
-# TODO:
-# - zoom
-# - pan
-# - resize
-# - auto-scale
-# - clean up
-#
-
-# apt-get install python-tk
-
-from Tkinter import *
-from tmc.decode import *
-from math import ceil, floor
-
-
-class channel:
-
- color_normal = "green"
- color_selected = "yellow"
- decode_color = "white"
- decode_bg_color = "#4040ff"
- decode_font = "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
-
- def __init__(self, main, number, data):
- self.main = main
- self.number = number
- self.tag = "d_%d" % number
- self.zoom_tag = "d_z_%d" % number
- self.d = data
- self.draw()
-
- def draw(self):
- if self.main.pos0 > len(self.d):
- return
- pos0 = max(0, self.main.pos0)
- last = self.d[pos0]
- line = [ self.x(pos0), self.y(last) ]
- for i in range(pos0, len(self.d)):
- if self.d[i] != last:
- line.extend((self.x(i), self.y(self.d[i])))
- x1 = self.x(i+1)
- if x1 > self.main.xres:
- break
- if self.d[i] == last and i != pos0:
- line.pop()
- line.pop()
- line.extend((x1, self.y(self.d[i])))
- last = self.d[i]
- self.main.w.create_line(
- fill = self.color_normal, tags = self.tag, *line)
-
- def draw_zoom(self):
- mag = float(self.main.xres)/self.main.samples
- last = self.d[0]
- line = [ 0, self.zoom_y(last) ]
- for i in range(0, len(self.d)):
- if self.d[i] != last:
- line.extend((i*mag, self.zoom_y(self.d[i])))
- elif i:
- line.pop()
- line.pop()
- line.extend(((i+1)*mag, self.zoom_y(self.d[i])))
- last = self.d[i]
- self.main.wz.create_line(
- fill = self.color_normal, tags = self.zoom_tag, *line)
-
- def redraw_zoom(self):
- self.main.w.delete(self.zoom_tag)
- self.draw_zoom()
-
- def redraw(self):
- self.main.w.delete(self.tag)
- self.draw()
-
- def x(self, sample):
- return int(round(self.main.x0+(sample-self.main.pos0)*self.main.mag))
-
- def y(self, value):
- return self.number*50-value*20+25
-
- def zoom_y(self, value):
- return self.number*6-value*2+5
-
- def select(self):
- self.main.w.itemconfig(self.tag, fill = self.color_selected)
-
- def deselect(self):
- self.main.w.itemconfig(self.tag, fill = self.color_normal)
-
- def set_decoder(self, decoder):
- self.decoder = decoder
-
- def begin_decode(self, pos):
- x0 = self.x(pos)
- x1 = x0+self.main.mag
- y = self.y(0)
- self.main.w.create_text(x0+5, y+6,
- fill = self.decode_color, anchor = "nw", tags = "d_fg_"+self.tag,
- font = self.decode_font)
- rect = self.main.w.create_rectangle(x0, y+5, x1, y+20,
- fill = self.decode_bg_color, outline = self.decode_bg_color,
- tags = "d_bg_"+self.tag)
- self.main.w.tag_raise("d_bg_"+self.tag)
- self.main.w.tag_raise("d_fg_"+self.tag)
-
- def decode(self, selected, bits):
- y = self.y(0)
- x0 = self.x(self.main.decode_from)
- x1 = self.x(self.main.cur.pos+1)
- if selected:
- text = d_counter(bits)
- else:
- text = self.decoder(bits)
- self.main.w.itemconfig("d_fg_"+self.tag, text = text)
- self.main.w.coords("d_bg_"+self.tag, x0, y+5, x1, y+20)
-
- def end_decode(self):
- self.main.w.delete("d_fg_"+self.tag)
- self.main.w.delete("d_bg_"+self.tag)
-
-
-class measurement:
-
- background_color = "black"
- color = "white"
- font = "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
- # sigh, where's a mono-spaced font with a sans-serif "1" when you need
- # one ...
- font = "-*-fixed-medium-r-normal-*-14-*-*-*-*-*-iso8859-*"
-
- def __init__(self, master, t0, dt, prefix = ""):
- self.fn = (
- ( self.show_samples, "Sa" ),
- ( self.show_time, "s" ),
- ( self.show_frequency, "Hz" ))
-
- self.t0 = t0
- self.dt = dt
- self.prefix = prefix
- self.last_prefix = None
- self.var = StringVar()
- self.button = Button(master, textvariable = self.var, width = 12,
- relief = FLAT, borderwidth = 0, font = self.font,
- fg = self.color, bg = self.background_color,
- activeforeground = self.color,
- activebackground = self.background_color,
- command = self.button)
- self.button.pack(side = LEFT)
- self.index = 0
- self.last = None
- self.hide()
-
- def button(self):
- self.index += 1
- self.index %= len(self.fn)
- self.__show(self.last)
-
- def pretty_float(self, f):
- if f < 0:
- sign = True
- f = -f
- else:
- sign = False
-
- if f < 1e-6:
- unit = "n"
- f *= 1e9
- elif f < 1e-3:
- unit = "u"
- f *= 1e6
- elif f < 1:
- unit = "m"
- f *= 1e3
- elif f < 1e3:
- unit = " "
- elif f < 1e6:
- unit = "k"
- f *= 1e-3
- elif f < 1e9:
- unit = "M"
- f *= 1e-6
- elif f < 1e12:
- unit = "G"
- f *= 1e-9
- elif f < 1e15:
- unit = "T"
- f *= 1e-12
- else:
- print f
- raise hell
-
- return "%s%7.3f%s" % ((" ", "-")[sign], f, unit)
-
- # Comment out the line above to remove trailing zeroes, e.g.,
- # 5.100 becomes 5.1. While this looks like a nice idea, I found the
- # constantly changing format somewhat irritating. It's also inaccurate
- # in the sense that these numbers represent physical units well above
- # quantum level and therefore cannot be exact.
-
- if round(f*1000) == round(f)*1000:
- dec = 0
- elif round(f*1000) == round(f*10)*100:
- dec = 1
- elif round(f*1000) == round(f*100)*10:
- dec = 2
- else:
- dec = 3
-
- fract = (0, 1+dec)[dec > 0]
-
- return "%s%*.*f%*s%s" % (
- (" ", "-")[sign],
- fract+3, dec, f,
- 4-fract, "",
- unit)
-
- def show_samples(self, samples):
- self.var.set(self.prefix+"%8d Sa" % samples)
-
- def show_time(self, samples):
- t = samples*self.dt+self.t0
- self.var.set(self.prefix+self.pretty_float(float(t))+"s")
-
- def show_frequency(self, samples):
- t = samples*self.dt+self.t0
- if abs(t) < 1e-12:
- self.hide()
- else:
- self.var.set(self.prefix+self.pretty_float(1.0/t)+"Hz")
-
- def __show(self, samples):
- if samples is None:
- self.hide()
- else:
- self.fn[self.index][0](samples)
-
- def show(self, samples):
- if samples != self.last or self.prefix != self.last_prefix:
- self.__show(samples)
- self.last = samples
- self.last_prefix = self.prefix
-
- def hide(self):
- self.var.set(" "*9+self.fn[self.index][1])
- self.last = None
-
- def remove(self):
- self.button(delete)
-
-
-class cursor:
-
- color = "white"
-
- def __init__(self, main):
- self.main = main
- self.tag = main.w.create_line(0, 0, 0, 0, fill = self.color, width = 1)
- self.zoom_tag = \
- main.wz.create_line(0, 0, 0, 0, fill = self.color, width = 1)
- self.x = 0
- self.pos = 0
-
- def set(self):
- x = self.main.ch[0].x(self.pos)
- self.main.w.coords(self.tag, x, 0, x, self.main.yres)
- self.main.w.tag_raise(self.tag)
- x = int(float(self.pos)/self.main.samples*self.main.xres)
- self.main.wz.coords(self.zoom_tag, x, 0, x, self.main.zres)
- self.main.wz.tag_raise(self.zoom_tag)
- self.main.meas_pos.show(self.pos)
-
- def x_to_pos(self, x):
- s = self.main.pos0+int(round((x-self.main.x0)/self.main.mag))
- if s < 0:
- return 0
- if s >= self.main.samples:
- return self.main.samples-1
- return s
-
- def move(self, x):
- s = self.x_to_pos(x)
- self.x = x
- if s != self.pos:
- self.pos = s
- self.set()
-
-
-class main_window:
-
- background_color = "black"
- selection_color = "#808080"
- data_color = "white"
- zoom_color = "#808080"
-
- def __init__(self, master, d, t0, sample_step):
- channels = len(d)
- self.samples = len(d[0])
- self.xres = self.samples
- self.yres = channels*50
- self.zres = channels*6+2
- self.geometry()
- self.w3 = self.control_window(master)
- self.wz = self.zoom_window(master)
- self.w = self.main_window(master)
- self.wt = self.tick_window(master)
- self.wd = self.meas_window(master, t0, sample_step)
- self.setup_events(master)
- self.decode_from = None
- self.ch = []
- self.cur = cursor(self)
- for ch in range(0, channels):
- self.ch.append(channel(self, ch, d[ch]))
- self.decoder_menu(self.w3, self.ch[-1], ch)
- self.selected = None
- self.update_zoom_area()
-
- def geometry(self):
- self.x0 = 0
- self.pos0 = 0
- self.mag = 1.0
-
- def main_window(self, master):
- w = Canvas(master,
- width = self.xres,
- height = self.yres, bg = self.background_color)
- w.pack(expand = 1, fill = "x")
- return w
-
- def zoom_window(self, master):
- w = Canvas(master,
- width = self.xres,
- height = self.zres, bg = self.zoom_color)
- w.pack(expand = 1, fill = "x")
- self.zoom_area = w.create_rectangle(0, 0, 0, 0,
- fill = self.background_color, width = 0)
- return w
-
- def meas_window(self, master, t0, sample_step):
- w = Frame(master, width = self.xres, height = 20,
- bg = self.background_color)
- w.pack(expand = 1, fill = "x")
-
- self.meas_start = measurement(w, 0, sample_step)
- self.meas_pos = measurement(w, t0, sample_step)
- self.meas_width = measurement(w, 0, sample_step)
-
- def tick_window(self, master):
- w = Canvas(master, width = self.xres, height = 20,
- bg = self.background_color)
- w.pack(expand = 1, fill = "x")
-
- def control_window(self, master):
- w = Frame(master, width = 135, bg = self.background_color)
- w.pack(side = LEFT, fill = BOTH, expand = 1)
- b = Button(master, text = "Quit", relief = FLAT, command = master.quit)
- b.place(x = 3, y = self.yres+16+self.zres+2)
-
- def setup_events(self, master):
- self.w.bind("<Motion>", self.move)
- self.w.bind("<Button-1>", self.button)
- self.w.bind("<Button-3>", self.center)
- self.w.bind("<Button-4>", self.zoom_in)
- self.w.bind("<Button-5>", self.zoom_out)
- self.w.bind("<Configure>", self.resize)
-
- self.wz.bind("<Button-1>", self.zoom_button)
- self.wz.bind("<Button-3>", self.zoom_button)
- self.wz.bind("<Button-4>", self.zoom_in)
- self.wz.bind("<Button-5>", self.zoom_out)
-
- master.bind("+", self.zoom_in)
- master.bind("=", self.zoom_in)
- master.bind("-", self.zoom_out)
- master.bind(".", self.center)
- master.bind("c", self.center)
-
- def decoder_menu(self, master, ch, n):
- mb = Menubutton(master, direction = RIGHT, text = decoders[0][0],
- relief = FLAT, width = 16)
- mb.place(x = 3, y = 50*n+3+self.zres+2)
- mb.menu = Menu(mb, tearoff = 0)
- mb["menu"] = mb.menu
-
- for dec in decoders:
-
- # Brilliant hack. Found it described here:
- # http://infohost.nmt.edu/tcc/help/pubs/tkinter/extra-args.html
-
- def handler(ch = ch, decoder = dec[1], button = mb, label = dec[0]):
- ch.set_decoder(decoder)
- button.config(text = label)
-
- mb.menu.add_command(label = dec[0], command = handler)
- ch.set_decoder(decoders[0][1])
-
- def move(self, event):
- self.cur.move(event.x)
- if self.decode_from is not None:
- self.decode()
- else:
- self.move_y(event.y)
- self.measure_width()
-
- def move_y(self, y):
- ny = y/50
- if ny >= len(self.ch):
- return
- if self.selected is not None:
- if self.selected.number == ny:
- return
- self.selected.deselect()
- self.selected = self.ch[ny]
- self.selected.select()
-
- def measure_width(self):
- if self.selected is None:
- self.meas_width.hide()
- return
- val = self.selected.d[self.cur.pos]
- pos = self.cur.pos
- i = pos-1
- while i >= 0 and self.selected.d[i] == val:
- i -= 1
- width = pos-i-1
- i = pos+1
- while i < self.samples and self.selected.d[i] == val:
- i += 1
- width += i-pos-1+1 # remove last sample, add center
- self.meas_width.prefix = ("L ", "H ")[val]
- self.meas_width.show(width)
-
- def zoom(self):
- self.pos0 = self.cur.pos-int(self.cur.x/self.mag)
- self.x0 = self.cur.x-int(self.mag*(self.cur.pos-self.pos0-0.5))
- self.redraw()
-
- def zoom_in(self, event):
- if self.decode_from is not None:
- return
- if self.mag > 10:
- return
- self.mag *= 2.0
- self.zoom()
-
- def zoom_out(self, event):
- if self.decode_from is not None:
- return
- if self.xres > self.samples*self.mag:
- if self.reset():
- self.zoom()
- else:
- self.mag /= 2.0
- self.zoom()
-
- def center(self, event):
- if self.decode_from is not None:
- return
- x = self.cur.x
- self.cur.x = self.xres/2
- self.zoom()
- self.cur.move(x)
-
- def zoom_button(self, event):
- if self.decode_from is not None:
- return
- self.cur.pos = int(float(event.x)/self.xres*self.samples+0.49)
- if self.cur.pos >= self.samples:
- self.cur.pos = self.samples-1
- self.center(event)
-
- def reset(self):
- end = (self.samples-self.pos0)*self.mag
- # Entire waveform is on screen
- if self.pos0 <= 0 and (self.samples-self.pos0)*self.mag <= self.xres:
- return False
-
- # No unused space surrounde waveform
- if self.pos0 >= 0 and end >= self.xres:
- return False
-
- pos = self.samples-int(self.xres/self.mag)
- if abs(pos-self.pos0) > abs(self.pos0):
- self.pos0 = 0
- else:
- self.pos0 = pos
- self.cur.move(self.cur.x)
- return True
-
- def resize(self, event):
- if self.decode_from is not None:
- self.end_decode()
- self.xres = event.width
- for ch in self.ch:
- ch.redraw_zoom()
- self.reset()
- self.zoom()
-
- def redraw(self):
- for ch in self.ch:
- ch.redraw()
- self.cur.move(self.cur.x)
- self.update_zoom_area()
-
- def update_zoom_area(self):
- self.wz.coords(self.zoom_area,
- int(float(self.pos0)/self.samples*self.xres), 0,
- int(ceil((self.pos0+self.xres/self.mag)/self.samples*self.xres)),
- self.zres)
-
- def button(self, event):
- if self.decode_from is None:
- self.begin_decode()
- else:
- self.end_decode()
-
- def begin_decode(self):
- # @@@FIXME: at mag < 1, this we're usually off by a pixel
- x0 = max(1, self.pos0+int((self.cur.x-self.x0)/self.mag))
- x1 = min(self.samples,
- self.pos0+int((self.cur.x-self.x0+1)/self.mag)+1)
- # print self.cur.pos, x0, x1
- for pos in range(x0, x1):
- if self.selected.d[pos-1] != self.selected.d[pos]:
- break
- else:
- return
- self.decode_from = pos
- for ch in self.ch:
- ch.begin_decode(pos)
- x0 = self.ch[0].x(pos)
- x1 = x0+self.mag
- rect = self.w.create_rectangle(x0, 0, x1, self.yres,
- fill = self.selection_color, outline = self.selection_color,
- tags = "selection")
- self.w.tag_lower(rect)
- self.w.tag_raise(self.cur)
- self.decode()
-
- def decode(self):
- self.meas_start.show(self.cur.pos-self.decode_from)
- if self.cur.pos < self.decode_from:
- return
- x0 = self.ch[0].x(self.decode_from)
- x1 = self.ch[0].x(self.cur.pos+1)
- self.w.coords("selection", x0, 0, x1, self.yres)
- for ch in self.ch:
- bits = []
- i = self.decode_from
- while i <= self.cur.pos:
- if self.selected.d[i-1] != self.selected.d[i] and \
- self.selected.d[i] == self.selected.d[self.decode_from]:
- if ch.d[i-1] == ch.d[i]:
- bits.append(ch.d[i])
- else:
- bits.append("X")
- i += 1
- ch.decode(ch is self.selected, bits)
-
- def end_decode(self):
- for ch in self.ch:
- ch.end_decode()
- self.w.delete("selection")
- self.decode_from = None
- self.meas_start.hide()
-
-
-class dplore:
-
- def __init__(self, channels, t0, sample_step, title = None):
- self.master = Tk()
- if title is not None:
- self.master.title(title)
- self.master.resizable(1, 0)
- self.main = main_window(self.master, channels, t0, sample_step)
- self.master.bind("q", self.quit)
- mainloop()
-
- def quit(self, event):
- self.master.quit()
Copied: developers/werner/ahrt/host/tmc/lib/dxplore.py (from rev 4646, developers/werner/ahrt/host/tmc/lib/dplore.py)
===================================================================
--- developers/werner/ahrt/host/tmc/lib/dxplore.py (rev 0)
+++ developers/werner/ahrt/host/tmc/lib/dxplore.py 2008-09-16 00:45:40 UTC (rev 4647)
@@ -0,0 +1,593 @@
+#
+# dxplore.py - GUI for interactive exploration of digital waveforms
+#
+# Copyright (C) 2008 by OpenMoko, Inc.
+# Written by Werner Almesberger <werner at openmoko.org>
+# All Rights Reserved
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+
+#
+# WORK IN PROGRESS !
+#
+# TODO:
+# - clean up
+# - fix off-by-one error in selection
+# - add inversion
+# - add deglitching
+#
+
+
+from Tkinter import *
+from tmc.decode import *
+from math import ceil, floor
+
+
+class channel:
+
+ color_normal = "green"
+ color_selected = "yellow"
+ decode_color = "white"
+ decode_bg_color = "#4040ff"
+ decode_font = "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+
+ def __init__(self, main, number, data):
+ self.main = main
+ self.number = number
+ self.tag = "d_%d" % number
+ self.zoom_tag = "d_z_%d" % number
+ self.d = data
+ self.draw()
+
+ def draw(self):
+ if self.main.pos0 > len(self.d):
+ return
+ pos0 = max(0, self.main.pos0)
+ last = self.d[pos0]
+ line = [ self.x(pos0), self.y(last) ]
+ for i in range(pos0, len(self.d)):
+ if self.d[i] != last:
+ line.extend((self.x(i), self.y(self.d[i])))
+ x1 = self.x(i+1)
+ if x1 > self.main.xres:
+ break
+ if self.d[i] == last and i != pos0:
+ line.pop()
+ line.pop()
+ line.extend((x1, self.y(self.d[i])))
+ last = self.d[i]
+ self.main.w.create_line(
+ fill = self.color_normal, tags = self.tag, *line)
+
+ def draw_zoom(self):
+ mag = float(self.main.xres)/self.main.samples
+ last = self.d[0]
+ line = [ 0, self.zoom_y(last) ]
+ for i in range(0, len(self.d)):
+ if self.d[i] != last:
+ line.extend((i*mag, self.zoom_y(self.d[i])))
+ elif i:
+ line.pop()
+ line.pop()
+ line.extend(((i+1)*mag, self.zoom_y(self.d[i])))
+ last = self.d[i]
+ self.main.wz.create_line(
+ fill = self.color_normal, tags = self.zoom_tag, *line)
+
+ def redraw_zoom(self):
+ self.main.w.delete(self.zoom_tag)
+ self.draw_zoom()
+
+ def redraw(self):
+ self.main.w.delete(self.tag)
+ self.draw()
+
+ def x(self, sample):
+ return int(round(self.main.x0+(sample-self.main.pos0)*self.main.mag))
+
+ def y(self, value):
+ return self.number*50-value*20+25
+
+ def zoom_y(self, value):
+ return self.number*6-value*2+5
+
+ def select(self):
+ self.main.w.itemconfig(self.tag, fill = self.color_selected)
+
+ def deselect(self):
+ self.main.w.itemconfig(self.tag, fill = self.color_normal)
+
+ def set_decoder(self, decoder):
+ self.decoder = decoder
+
+ def begin_decode(self, pos):
+ x0 = self.x(pos)
+ x1 = x0+self.main.mag
+ y = self.y(0)
+ self.main.w.create_text(x0+5, y+6,
+ fill = self.decode_color, anchor = "nw", tags = "d_fg_"+self.tag,
+ font = self.decode_font)
+ rect = self.main.w.create_rectangle(x0, y+5, x1, y+20,
+ fill = self.decode_bg_color, outline = self.decode_bg_color,
+ tags = "d_bg_"+self.tag)
+ self.main.w.tag_raise("d_bg_"+self.tag)
+ self.main.w.tag_raise("d_fg_"+self.tag)
+
+ def decode(self, selected, bits):
+ y = self.y(0)
+ x0 = self.x(self.main.decode_from)
+ x1 = self.x(self.main.cur.pos+1)
+ if selected:
+ text = d_counter(bits)
+ else:
+ text = self.decoder(bits)
+ self.main.w.itemconfig("d_fg_"+self.tag, text = text)
+ self.main.w.coords("d_bg_"+self.tag, x0, y+5, x1, y+20)
+
+ def end_decode(self):
+ self.main.w.delete("d_fg_"+self.tag)
+ self.main.w.delete("d_bg_"+self.tag)
+
+
+class measurement:
+
+ background_color = "black"
+ color = "white"
+ font = "-*-helvetica-medium-r-*-*-12-*-*-*-*-*-*-*"
+ # sigh, where's a mono-spaced font with a sans-serif "1" when you need
+ # one ...
+ font = "-*-fixed-medium-r-normal-*-14-*-*-*-*-*-iso8859-*"
+
+ def __init__(self, master, t0, dt, prefix = ""):
+ self.fn = (
+ ( self.show_samples, "Sa" ),
+ ( self.show_time, "s" ),
+ ( self.show_frequency, "Hz" ))
+
+ self.t0 = t0
+ self.dt = dt
+ self.prefix = prefix
+ self.last_prefix = None
+ self.var = StringVar()
+ self.button = Button(master, textvariable = self.var, width = 12,
+ relief = FLAT, borderwidth = 0, font = self.font,
+ fg = self.color, bg = self.background_color,
+ activeforeground = self.color,
+ activebackground = self.background_color,
+ command = self.button)
+ self.button.pack(side = LEFT)
+ self.index = 0
+ self.last = None
+ self.hide()
+
+ def button(self):
+ self.index += 1
+ self.index %= len(self.fn)
+ self.__show(self.last)
+
+ def pretty_float(self, f):
+ if f < 0:
+ sign = True
+ f = -f
+ else:
+ sign = False
+
+ if f < 1e-6:
+ unit = "n"
+ f *= 1e9
+ elif f < 1e-3:
+ unit = "u"
+ f *= 1e6
+ elif f < 1:
+ unit = "m"
+ f *= 1e3
+ elif f < 1e3:
+ unit = " "
+ elif f < 1e6:
+ unit = "k"
+ f *= 1e-3
+ elif f < 1e9:
+ unit = "M"
+ f *= 1e-6
+ elif f < 1e12:
+ unit = "G"
+ f *= 1e-9
+ elif f < 1e15:
+ unit = "T"
+ f *= 1e-12
+ else:
+ print f
+ raise hell
+
+ return "%s%7.3f%s" % ((" ", "-")[sign], f, unit)
+
+ # Comment out the line above to remove trailing zeroes, e.g.,
+ # 5.100 becomes 5.1. While this looks like a nice idea, I found the
+ # constantly changing format somewhat irritating. It's also inaccurate
+ # in the sense that these numbers represent physical units well above
+ # quantum level and therefore cannot be exact.
+
+ if round(f*1000) == round(f)*1000:
+ dec = 0
+ elif round(f*1000) == round(f*10)*100:
+ dec = 1
+ elif round(f*1000) == round(f*100)*10:
+ dec = 2
+ else:
+ dec = 3
+
+ fract = (0, 1+dec)[dec > 0]
+
+ return "%s%*.*f%*s%s" % (
+ (" ", "-")[sign],
+ fract+3, dec, f,
+ 4-fract, "",
+ unit)
+
+ def show_samples(self, samples):
+ self.var.set(self.prefix+"%8d Sa" % samples)
+
+ def show_time(self, samples):
+ t = samples*self.dt+self.t0
+ self.var.set(self.prefix+self.pretty_float(float(t))+"s")
+
+ def show_frequency(self, samples):
+ t = samples*self.dt+self.t0
+ if abs(t) < 1e-12:
+ self.hide()
+ else:
+ self.var.set(self.prefix+self.pretty_float(1.0/t)+"Hz")
+
+ def __show(self, samples):
+ if samples is None:
+ self.hide()
+ else:
+ self.fn[self.index][0](samples)
+
+ def show(self, samples):
+ if samples != self.last or self.prefix != self.last_prefix:
+ self.__show(samples)
+ self.last = samples
+ self.last_prefix = self.prefix
+
+ def hide(self):
+ self.var.set(" "*9+self.fn[self.index][1])
+ self.last = None
+
+ def remove(self):
+ self.button(delete)
+
+
+class cursor:
+
+ color = "white"
+
+ def __init__(self, main):
+ self.main = main
+ self.tag = main.w.create_line(0, 0, 0, 0, fill = self.color, width = 1)
+ self.zoom_tag = \
+ main.wz.create_line(0, 0, 0, 0, fill = self.color, width = 1)
+ self.x = 0
+ self.pos = 0
+
+ def set(self):
+ x = self.main.ch[0].x(self.pos)
+ self.main.w.coords(self.tag, x, 0, x, self.main.yres)
+ self.main.w.tag_raise(self.tag)
+ x = int(float(self.pos)/self.main.samples*self.main.xres)
+ self.main.wz.coords(self.zoom_tag, x, 0, x, self.main.zres)
+ self.main.wz.tag_raise(self.zoom_tag)
+ self.main.meas_pos.show(self.pos)
+
+ def x_to_pos(self, x):
+ s = self.main.pos0+int(round((x-self.main.x0)/self.main.mag))
+ if s < 0:
+ return 0
+ if s >= self.main.samples:
+ return self.main.samples-1
+ return s
+
+ def move(self, x):
+ s = self.x_to_pos(x)
+ self.x = x
+ if s != self.pos:
+ self.pos = s
+ self.set()
+
+
+class main_window:
+
+ background_color = "black"
+ selection_color = "#808080"
+ data_color = "white"
+ zoom_color = "#808080"
+
+ def __init__(self, master, d, t0, sample_step):
+ channels = len(d)
+ self.samples = len(d[0])
+ self.xres = self.samples
+ self.yres = channels*50
+ self.zres = channels*6+2
+ self.geometry()
+ self.w3 = self.control_window(master)
+ self.wz = self.zoom_window(master)
+ self.w = self.main_window(master)
+ self.wt = self.tick_window(master)
+ self.wd = self.meas_window(master, t0, sample_step)
+ self.setup_events(master)
+ self.decode_from = None
+ self.ch = []
+ self.cur = cursor(self)
+ for ch in range(0, channels):
+ self.ch.append(channel(self, ch, d[ch]))
+ self.decoder_menu(self.w3, self.ch[-1], ch)
+ self.selected = None
+ self.update_zoom_area()
+
+ def geometry(self):
+ self.x0 = 0
+ self.pos0 = 0
+ self.mag = 1.0
+
+ def main_window(self, master):
+ w = Canvas(master,
+ width = self.xres,
+ height = self.yres, bg = self.background_color)
+ w.pack(expand = 1, fill = "x")
+ return w
+
+ def zoom_window(self, master):
+ w = Canvas(master,
+ width = self.xres,
+ height = self.zres, bg = self.zoom_color)
+ w.pack(expand = 1, fill = "x")
+ self.zoom_area = w.create_rectangle(0, 0, 0, 0,
+ fill = self.background_color, width = 0)
+ return w
+
+ def meas_window(self, master, t0, sample_step):
+ w = Frame(master, width = self.xres, height = 20,
+ bg = self.background_color)
+ w.pack(expand = 1, fill = "x")
+
+ self.meas_start = measurement(w, 0, sample_step)
+ self.meas_pos = measurement(w, t0, sample_step)
+ self.meas_width = measurement(w, 0, sample_step)
+
+ def tick_window(self, master):
+ w = Canvas(master, width = self.xres, height = 20,
+ bg = self.background_color)
+ w.pack(expand = 1, fill = "x")
+
+ def control_window(self, master):
+ w = Frame(master, width = 135, bg = self.background_color)
+ w.pack(side = LEFT, fill = BOTH, expand = 1)
+ b = Button(master, text = "Quit", relief = FLAT, command = master.quit)
+ b.place(x = 3, y = self.yres+16+self.zres+2)
+
+ def setup_events(self, master):
+ self.w.bind("<Motion>", self.move)
+ self.w.bind("<Button-1>", self.button)
+ self.w.bind("<Button-3>", self.center)
+ self.w.bind("<Button-4>", self.zoom_in)
+ self.w.bind("<Button-5>", self.zoom_out)
+ self.w.bind("<Configure>", self.resize)
+
+ self.wz.bind("<Button-1>", self.zoom_button)
+ self.wz.bind("<Button-3>", self.zoom_button)
+ self.wz.bind("<Button-4>", self.zoom_in)
+ self.wz.bind("<Button-5>", self.zoom_out)
+
+ master.bind("+", self.zoom_in)
+ master.bind("=", self.zoom_in)
+ master.bind("-", self.zoom_out)
+ master.bind(".", self.center)
+ master.bind("c", self.center)
+
+ def decoder_menu(self, master, ch, n):
+ mb = Menubutton(master, direction = RIGHT, text = decoders[0][0],
+ relief = FLAT, width = 16)
+ mb.place(x = 3, y = 50*n+3+self.zres+2)
+ mb.menu = Menu(mb, tearoff = 0)
+ mb["menu"] = mb.menu
+
+ for dec in decoders:
+
+ # Brilliant hack. Found it described here:
+ # http://infohost.nmt.edu/tcc/help/pubs/tkinter/extra-args.html
+
+ def handler(ch = ch, decoder = dec[1], button = mb, label = dec[0]):
+ ch.set_decoder(decoder)
+ button.config(text = label)
+
+ mb.menu.add_command(label = dec[0], command = handler)
+ ch.set_decoder(decoders[0][1])
+
+ def move(self, event):
+ self.cur.move(event.x)
+ if self.decode_from is not None:
+ self.decode()
+ else:
+ self.move_y(event.y)
+ self.measure_width()
+
+ def move_y(self, y):
+ ny = y/50
+ if ny >= len(self.ch):
+ return
+ if self.selected is not None:
+ if self.selected.number == ny:
+ return
+ self.selected.deselect()
+ self.selected = self.ch[ny]
+ self.selected.select()
+
+ def measure_width(self):
+ if self.selected is None:
+ self.meas_width.hide()
+ return
+ val = self.selected.d[self.cur.pos]
+ pos = self.cur.pos
+ i = pos-1
+ while i >= 0 and self.selected.d[i] == val:
+ i -= 1
+ width = pos-i-1
+ i = pos+1
+ while i < self.samples and self.selected.d[i] == val:
+ i += 1
+ width += i-pos-1+1 # remove last sample, add center
+ self.meas_width.prefix = ("L ", "H ")[val]
+ self.meas_width.show(width)
+
+ def zoom(self):
+ self.pos0 = self.cur.pos-int(self.cur.x/self.mag)
+ self.x0 = self.cur.x-int(self.mag*(self.cur.pos-self.pos0-0.5))
+ self.redraw()
+
+ def zoom_in(self, event):
+ if self.decode_from is not None:
+ return
+ if self.mag > 10:
+ return
+ self.mag *= 2.0
+ self.zoom()
+
+ def zoom_out(self, event):
+ if self.decode_from is not None:
+ return
+ if self.xres > self.samples*self.mag:
+ if self.reset():
+ self.zoom()
+ else:
+ self.mag /= 2.0
+ self.zoom()
+
+ def center(self, event):
+ if self.decode_from is not None:
+ return
+ x = self.cur.x
+ self.cur.x = self.xres/2
+ self.zoom()
+ self.cur.move(x)
+
+ def zoom_button(self, event):
+ if self.decode_from is not None:
+ return
+ self.cur.pos = int(float(event.x)/self.xres*self.samples+0.49)
+ if self.cur.pos >= self.samples:
+ self.cur.pos = self.samples-1
+ self.center(event)
+
+ def reset(self):
+ end = (self.samples-self.pos0)*self.mag
+ # Entire waveform is on screen
+ if self.pos0 <= 0 and (self.samples-self.pos0)*self.mag <= self.xres:
+ return False
+
+ # No unused space surrounde waveform
+ if self.pos0 >= 0 and end >= self.xres:
+ return False
+
+ pos = self.samples-int(self.xres/self.mag)
+ if abs(pos-self.pos0) > abs(self.pos0):
+ self.pos0 = 0
+ else:
+ self.pos0 = pos
+ self.cur.move(self.cur.x)
+ return True
+
+ def resize(self, event):
+ if self.decode_from is not None:
+ self.end_decode()
+ self.xres = event.width
+ for ch in self.ch:
+ ch.redraw_zoom()
+ self.reset()
+ self.zoom()
+
+ def redraw(self):
+ for ch in self.ch:
+ ch.redraw()
+ self.cur.move(self.cur.x)
+ self.update_zoom_area()
+
+ def update_zoom_area(self):
+ self.wz.coords(self.zoom_area,
+ int(float(self.pos0)/self.samples*self.xres), 0,
+ int(ceil((self.pos0+self.xres/self.mag)/self.samples*self.xres)),
+ self.zres)
+
+ def button(self, event):
+ if self.decode_from is None:
+ self.begin_decode()
+ else:
+ self.end_decode()
+
+ def begin_decode(self):
+ # @@@FIXME: at mag < 1, this we're usually off by a pixel
+ x0 = max(1, self.pos0+int((self.cur.x-self.x0)/self.mag))
+ x1 = min(self.samples,
+ self.pos0+int((self.cur.x-self.x0+1)/self.mag)+1)
+ # print self.cur.pos, x0, x1
+ for pos in range(x0, x1):
+ if self.selected.d[pos-1] != self.selected.d[pos]:
+ break
+ else:
+ return
+ self.decode_from = pos
+ for ch in self.ch:
+ ch.begin_decode(pos)
+ x0 = self.ch[0].x(pos)
+ x1 = x0+self.mag
+ rect = self.w.create_rectangle(x0, 0, x1, self.yres,
+ fill = self.selection_color, outline = self.selection_color,
+ tags = "selection")
+ self.w.tag_lower(rect)
+ self.w.tag_raise(self.cur)
+ self.decode()
+
+ def decode(self):
+ self.meas_start.show(self.cur.pos-self.decode_from)
+ if self.cur.pos < self.decode_from:
+ return
+ x0 = self.ch[0].x(self.decode_from)
+ x1 = self.ch[0].x(self.cur.pos+1)
+ self.w.coords("selection", x0, 0, x1, self.yres)
+ for ch in self.ch:
+ bits = []
+ i = self.decode_from
+ while i <= self.cur.pos:
+ if self.selected.d[i-1] != self.selected.d[i] and \
+ self.selected.d[i] == self.selected.d[self.decode_from]:
+ if ch.d[i-1] == ch.d[i]:
+ bits.append(ch.d[i])
+ else:
+ bits.append("X")
+ i += 1
+ ch.decode(ch is self.selected, bits)
+
+ def end_decode(self):
+ for ch in self.ch:
+ ch.end_decode()
+ self.w.delete("selection")
+ self.decode_from = None
+ self.meas_start.hide()
+
+
+class dxplore:
+
+ def __init__(self, channels, t0, sample_step, title = None):
+ self.master = Tk()
+ if title is not None:
+ self.master.title(title)
+ self.master.resizable(1, 0)
+ self.main = main_window(self.master, channels, t0, sample_step)
+ self.master.bind("q", self.quit)
+ mainloop()
+
+ def quit(self, event):
+ self.master.quit()
Modified: developers/werner/ahrt/host/tmc/setup.py
===================================================================
--- developers/werner/ahrt/host/tmc/setup.py 2008-09-16 00:11:17 UTC (rev 4646)
+++ developers/werner/ahrt/host/tmc/setup.py 2008-09-16 00:45:40 UTC (rev 4647)
@@ -5,7 +5,7 @@
description = "Test and Measurement Control",
py_modules = [ "tmc.instrument",
"tmc.wave", "tmc.trigger", "tmc.shape",
- "tmc.decode", "tmc.dplore",
+ "tmc.decode", "tmc.dxplore",
"tmc.meter", "tmc.scope", "tmc.power", "tmc.function" ],
package_dir = { "tmc": "lib" },
ext_modules = [
More information about the commitlog
mailing list