r4646 - in developers/werner/ahrt/host/tmc: . demo lib

werner at docs.openmoko.org werner at docs.openmoko.org
Tue Sep 16 02:11:18 CEST 2008


Author: werner
Date: 2008-09-16 02:11:17 +0200 (Tue, 16 Sep 2008)
New Revision: 4646

Added:
   developers/werner/ahrt/host/tmc/lib/decode.py
   developers/werner/ahrt/host/tmc/lib/dplore.py
Modified:
   developers/werner/ahrt/host/tmc/README
   developers/werner/ahrt/host/tmc/demo/dplore.py
   developers/werner/ahrt/host/tmc/lib/trigger.py
   developers/werner/ahrt/host/tmc/lib/wave.py
   developers/werner/ahrt/host/tmc/setup.py
Log:
Mainly dplore restructuring and making it useful in real life.

Highlights:
- fixed a few stupid bugs that made dplore painfully slow
- split dplore.py into components and prepared the SDIO command decoder for
  batch processing
- added measurements and zoom
- lots of digitizing and sampling fixes

Details:
- demo/dplore.py (channel.__init__): draw waveforms as single polyline and
  omit redundant segments
- demo/dplore.py (d_sdio_cmd): fixed handling of CRC containing invalid bits
- demo/dplore.py (main_window.move): fixed index overrun as lower end of
  window
- demo/dplore.py (main_window.move): deselect/select only if selection
  changed, not on every movement
- demo/dplore.py (main_window): cleaned up creating of the various windows
- demo/dplore.py (main_window, measurement): added a measurement window
  measuring selection width, current position, and current interval
- demo/dplore.py (dplore.quit): do actually call it and exit more gracefully
- demo/dplore.py (control_window): added a "quit" button
- demo/dplore.py, lib/dplore.py,  lib/decode.py: split dplore into its
  components and fixed bugs concealed by having everything in a single file
- lib/decode.py (d_sdio_cmd): new option "stop" to stop processing at the end
  of the command or at the first error
- README: added install instructions
- lib/wave.py (digital.sample): add samples for constant tails
- lib/wave.py (digital.sample): make sure to return numeric values if constant
- lib/wave.py (digital.sample): use list multiplication instead of map+range
- lib/wave.py (analog.digitize): don't try to optimize calls to res.append, or
  constant tails will get truncated



Modified: developers/werner/ahrt/host/tmc/README
===================================================================
--- developers/werner/ahrt/host/tmc/README	2008-09-16 00:08:57 UTC (rev 4645)
+++ developers/werner/ahrt/host/tmc/README	2008-09-16 00:11:17 UTC (rev 4646)
@@ -40,13 +40,49 @@
 annotated examples in the demo/ directory.
 
 
-System setup
+Installation
 ============
 
+Prerequisites
+-------------
+
+To build TMC, development libraries for libc, Python, and libusb are
+required.  On Ubuntu, one would install them with:
+
+# apt-get install libc6-dev python-dev libusb-dev
+
+At run time, some parts of TMC use NumPy, Tkinter, and ImageMagick.
+On Ubuntu, one would install them with:
+
+# apt-get install python-numpy python-tk imagemagick
+
+Waveforms are saved in a format compatible with gnuplot.
+On Ubuntu, one would install gnuplot with:
+
+# apt-get install gnuplot
+
+
+Building and installing TMC
+---------------------------
+
+TMC is built by simply running "make":
+
+% cd tmc
+% make
+
+To install TMC on the local machine, run the following command as root:
+
+# make install
+
+
+System setup
+------------
+
 Access to USB TMC devices requires that the user running TMC has write
-access to the corresponding device files. This is accomplished by
-making all these device files belong to a group "usbtmc" and making
-TMC users members of this group.
+access to the corresponding device files. This is accomplished either
+by using TMC from a privileged account (i.e., root), which is generally
+considered unsafe practice, or by making all these device files belong
+to a group "usbtmc" and making TMC users members of this group.
 
 The instructions below apply to Ubuntu 8.04. The procedure may differ
 slightly for other systems, and it will differ a lot for systems not

Modified: developers/werner/ahrt/host/tmc/demo/dplore.py
===================================================================
--- developers/werner/ahrt/host/tmc/demo/dplore.py	2008-09-16 00:08:57 UTC (rev 4645)
+++ developers/werner/ahrt/host/tmc/demo/dplore.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -4,403 +4,44 @@
 # WORK IN PROGRESS !
 #
 # TODO:
-# - zoom
-# - pan
-# - resize
-# - auto-scale
-# - turn this into a module
-# - clean up
 #
+# - 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
+#
 
-# apt-get install python-tk
-
-from Tkinter import *
+from tmc.dplore import dplore
 from tmc.wave import waves
-import sys
+from sys import argv
 
 
-def d_bits(bits):
-    s = ""
-    for b in bits:
-	s += str(b)
-    return s
+w = waves()
+w.load(argv[1])
 
-def d_byte(bit_pos, bits):
-    s = ""
+dig = []
+for wv in w:
+    dig.append(wv.digitize(0.5))
 
-    for i in range(0, len(bits)-7, 8):
-	if len(s):
-	    s += "."
-        v = 0
-	for j in range(0, 8):
-	    if bits[i+j] == "X":
-		s += "??"
-		break
-	    v |= bits[i+j] << bit_pos(j)
-	else:
-	    s += "%02X" % v
-
-    if len(bits) & 7:
-	s += "|"+d_bits(bits[len(bits) & ~7:len(bits)])
-    return s
-
-def d_byte_lsb(bits):
-    return d_byte(lambda p: p, bits)
-
-def d_byte_msb(bits):
-    return d_byte(lambda p: 7-p, bits)
-
-def d_counter(bits):
-    return "%d" % len(bits)
-
-def d_msb(bits):
-    v = 0
-    pos = len(bits)-1
-    for b in bits:
-	if b == "X":
-	    return None
-	v |= b << pos
-	pos -= 1
-    return v
-
-def decode_address(addr):
-    if addr < 0x14:
-	return (
-	    "Rev", "SDR", "IOE", "IOR",
-	    "IEN", "INT", "Abr", "Bus",
-            "Cap", "CIS", "Sus", "Sel",
-	    "EXF", "RDY", "BSZ", "BSZ",
-	    "PWR", "HSp")[addr]
-    return None
-
-def decode_arg(cmd, arg):
-    if cmd == 52:
-	write = (arg >> 31) & 1
-	s = ("R", "W")[write]
-	s += str((arg >> 28) & 7)
-	s += (" ", "W")[(arg >> 27) & 1]
-	addr = (arg >> 9) & 0x1ffff
-	name = decode_address(addr)
-	if name is None:
-	    s += "0x%X" % addr
-	else:
-	    s += name+("(0x%X)" % addr)
-	if not write:
-	    return s
-	return s+",0x%X" % (arg & 0xff)
-    if cmd == 53:
-	write = (arg >> 31) & 1
-	s = ("R", "W")[write]
-	s += str((arg >> 28) & 7)
-	s += ("b", "B")[(arg >> 27) & 1]
-	s += ("=", "+")[(arg >> 26) & 1]
-	addr = (arg >> 9) & 0x1ffff
-	name = decode_address(addr)
-	if name is None:
-	    s += "0x%X" % addr
-	else:
-	    s += name+("(0x%X)" % addr)
-	if not write:
-	    return s
-	return s+"*%d" % (arg & 0x1ff)
-    return None
-
-def d_sdio_cmd(bits):
-    if bits[0]:
-	s = "~"
-    else:
-	s = ""
-
-    while len(bits) and bits[0]:
-	del bits[0]
-
-    if len(bits) == 0:
-	return s
-    s += "["
-    del bits[0]
-
-    if len(bits) == 0:
-	return s
-    s += ("?", ">")[bits[0]]
-    del bits[0]
-
-    if len(bits) == 0:
-	return s
-    if len(bits) < 6:
-	return s+"|"+d_bits(bits)
-    cmd = d_msb(bits[0:6])
-    if cmd is None:
-	s += "CMD??"
-    else:
-	s += "CMD%d" % cmd
-    del bits[0:6]
-
-    if len(bits) == 0:
-	return s
-    if len(bits) < 32:
-	return s+"("+d_byte_msb(bits)
-    arg = d_msb(bits[0:32])
-    if cmd is None or arg is None:
-	arg = None
-    else:
-	arg = decode_arg(cmd, arg)
-    if arg is None:
-	s += "("+d_byte_msb(bits)+")"
-    else:
-	s += "("+arg+")"
-    del bits[0:32]
-
-    if len(bits) == 0:
-	return s
-    if len(bits) < 7:
-	return s+"|"+d_bits(bits)
-    crc = d_msb(bits[0:7])
-    s += "CRC%02x" % crc
-    del bits[0:7]
-
-    if len(bits) == 0:
-	return s
-    s += ("?", "]")[bits[0]]
-    del bits[0]
-
-    if len(bits):
-	s += "|"+d_bits(bits)
-
-    return s
-
-def d_sdio_resp(bits):
-    return "SDIO"
-
-decoders = [
-    ( "LSB->MSB", d_byte_lsb ),
-    ( "MSB->LSB", d_byte_msb ),
-    ( "SDIO CMD", d_sdio_cmd ),
-    ( "SDIO RESP", d_sdio_resp )
-]
-
-class channel:
-
-    color_normal = "green"
-    color_selected = "yellow"
-    decode_color = "white"
-    decode_bg_color = "#4040ff"
-
-    def __init__(self, main, number, data):
-	self.main = main
-	self.number = number
-	self.tag = "ch%d" % number
-	self.d = data
-
-	last = data[0]
-	for i in range(0, len(data)):
-	    if data[i] != last:
-		main.w.create_line(self.x(i), self.y(0), self.x(i),
-		  self.y(1), fill = self.color_normal, tags = self.tag)
-	    main.w.create_line(self.x(i), self.y(data[i]),
-	      self.x(i+1), self.y(data[i]),
-	      fill = self.color_normal, tags = self.tag)
-#	    main.w.create_line(self.x(i), self.y(last),
-#	      self.x(i+1), self.y(data[i]),
-#	      fill = self.color_normal, tags = self.tag)
-	    last = data[i]
-
-    def x(self, sample):
-	return sample*self.main.mag
-
-    def y(self, value):
-	return self.number*50-value*20+25
-
-    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)
-	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 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.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)
-
-    def move(self, x):
-	s = (x+self.main.mag/2)/self.main.mag
-	if s >= len(self.main.ch[0].d):
-	    return
-	if s != self.pos:
-	    self.pos = s
-	    self.set()
-
-
-class main_window:
-
-    background_color = "black"
-    selection_color = "#808080"
-
-    def __init__(self, master, d):
-	channels = len(d)
-	samples = len(d[0])
-	self.mag = 1
-	self.yres = channels*50
-	self.w3 = Frame(master, width = 100, bg = 'black')
-	self.w3.pack(side = LEFT, fill = BOTH, expand = 1)
-	self.w = Canvas(master,
-	  width = samples*self.mag,
-	  height = self.yres, bg = self.background_color)
-	self.w.pack(expand = 1, fill = "x")
-	self.w2 = Canvas(master, width = samples*self.mag, height = 20,
-	  bg = 'black')
-	self.w2.pack(expand = 1, fill = "x")
-	self.w.bind("<Motion>", self.move)
-	self.w.bind("<Button-1>", self.button)
-	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
-
-    def decoder_menu(self, master, ch, n):
-	mb = Menubutton(master, direction = RIGHT, text = decoders[0][0],
-	  relief = FLAT, width = 10)
-	mb.place(x = 3, y = 50*n+3)
-	mb.menu = Menu(mb, tearoff = 0)
-	mb["menu"] = mb.menu
-
-	for dec in decoders:
-	    def handler(ch = ch, decoder = dec[1], button = mb, label = dec[0]):
-		ch.set_decoder(decoder)
-		mb.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)
-	y = event.y
-	ny = (event.y+0)/50
-	if self.decode_from is not None:
-	    self.decode()
-	    return
-	if self.selected is not None:
-	    self.selected.deselect()
-	self.selected = self.ch[ny]
-	self.selected.select()
-
-    def button(self, event):
-	if self.decode_from is None:
-	    self.begin_decode()
-	else:
-	    self.end_decode()
-	
-    def begin_decode(self):
-	pos = self.cur.pos
-	if pos == 0:
-	    return
-	if self.selected.d[pos-1] == self.selected.d[pos]:
-	    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):
-	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
-
-
-class dplore:
-
-    def __init__(self, channels):
-	self.master = Tk()
-	self.master.resizable(1, 0)
-	self.main = main_window(self.master, d)
-	self.master.bind("q", quit)
-	mainloop()
-
-    def quit(event):
-	sys.exit(0)
-
-
-w = waves()
-w.load(sys.argv[1])
-min = w[1].digitize(0.5).sample_step()
-for wv in w[2:]:
-    step = wv.digitize(0.5).sample_step()
+min = dig[1].sample_step()
+for d in dig[2:]:
+    step = d.sample_step()
     if step is not None and step < min:
 	min = step
-d = []
-for wv in w[1:]:
-    a = wv.digitize(0.5).sample(min)
+
+da = []
+for d in dig[1:]:
+    a = d.sample(min)
 #    print len(a)
     if len(a) >= 1500:
-	d.append(a[0:1500])
-dplore(d.reverse())
+	da.append(a)
 
-# 1, 2, 3, 5, 10
+da.reverse()
+dplore(da, dig[1].start(), min, argv[1])

Added: developers/werner/ahrt/host/tmc/lib/decode.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/decode.py	                        (rev 0)
+++ developers/werner/ahrt/host/tmc/lib/decode.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -0,0 +1,194 @@
+#!/usr/bin/python
+
+
+def d_bits(bits):
+    s = ""
+    for b in bits:
+	s += str(b)
+    return s
+
+
+def d_byte(bit_pos, bits):
+    s = ""
+
+    for i in range(0, len(bits)-7, 8):
+	if len(s):
+	    s += "."
+        v = 0
+	for j in range(0, 8):
+	    if bits[i+j] == "X":
+		s += "??"
+		break
+	    v |= bits[i+j] << bit_pos(j)
+	else:
+	    s += "%02X" % v
+
+    if len(bits) & 7:
+	s += "|"+d_bits(bits[len(bits) & ~7:len(bits)])
+    return s
+
+
+def d_byte_lsb(bits):
+    return d_byte(lambda p: p, bits)
+
+
+def d_byte_msb(bits):
+    return d_byte(lambda p: 7-p, bits)
+
+
+def d_counter(bits):
+    return "%d" % len(bits)
+
+
+def d_msb(bits):
+    v = 0
+    pos = len(bits)-1
+    for b in bits:
+	if b == "X":
+	    return None
+	v |= b << pos
+	pos -= 1
+    return v
+
+
+def decode_address(addr):
+    if addr < 0x14:
+	return (
+	    "Rev", "SDR", "IOE", "IOR",
+	    "IEN", "INT", "Abr", "Bus",
+            "Cap", "CIS", "Sus", "Sel",
+	    "EXF", "RDY", "BSZ", "BSZ",
+	    "PWR", "HSp")[addr]
+    return None
+
+
+def decode_arg(cmd, arg):
+    if cmd == 52:
+	write = (arg >> 31) & 1
+	s = ("R", "W")[write]
+	s += str((arg >> 28) & 7)
+	s += (" ", "W")[(arg >> 27) & 1]
+	addr = (arg >> 9) & 0x1ffff
+	name = decode_address(addr)
+	if name is None:
+	    s += "0x%X" % addr
+	else:
+	    s += name+("(0x%X)" % addr)
+	if not write:
+	    return s
+	return s+",0x%X" % (arg & 0xff)
+    if cmd == 53:
+	write = (arg >> 31) & 1
+	s = ("R", "W")[write]
+	s += str((arg >> 28) & 7)
+	s += ("b", "B")[(arg >> 27) & 1]
+	s += ("=", "+")[(arg >> 26) & 1]
+	addr = (arg >> 9) & 0x1ffff
+	name = decode_address(addr)
+	if name is None:
+	    s += "0x%X" % addr
+	else:
+	    s += name+("(0x%X)" % addr)
+	if not write:
+	    return s
+	return s+"*%d" % (arg & 0x1ff)
+    return None
+
+
+def d_sdio_cmd(bits, stop = False):
+    if bits[0]:
+	s = "~"
+    else:
+	s = ""
+
+    while len(bits) and bits[0]:
+	del bits[0]
+
+    if len(bits) == 0:
+	return s
+    s += "["
+    del bits[0]
+
+    if len(bits) == 0:
+	return s
+    if bits[0]:
+	s += ">"
+    else:
+	s += "?"
+	if stop:
+	    return s
+    del bits[0]
+
+    if len(bits) == 0:
+	return s
+    if len(bits) < 6:
+	return s+"|"+d_bits(bits)
+    cmd = d_msb(bits[0:6])
+    if cmd is None:
+	s += "CMD??"
+	if stop:
+	    return s
+    else:
+	s += "CMD%d" % cmd
+    del bits[0:6]
+
+    if len(bits) == 0:
+	return s
+    if len(bits) < 32:
+	return s+"("+d_byte_msb(bits)
+    arg = d_msb(bits[0:32])
+    if cmd is None or arg is None:
+	arg = None
+    else:
+	arg = decode_arg(cmd, arg)
+    if arg is None:
+	s += "("+d_byte_msb(bits)+")"
+	if stop:
+	    return s
+    else:
+	s += "("+arg+")"
+    del bits[0:32]
+
+    if len(bits) == 0:
+	return s
+    if len(bits) < 7:
+	return s+"|"+d_bits(bits)
+    crc = d_msb(bits[0:7])
+    if crc is None:
+	s += "CRC??"
+	if stop:
+	    return s
+    else:
+	s += "CRC%02x" % crc
+    del bits[0:7]
+
+    if len(bits) == 0:
+	return s
+    s += ("?", "]")[bits[0]]
+    del bits[0]
+
+    if stop:
+	return s
+
+    if len(bits):
+	s += "|"+d_bits(bits)
+
+    return s
+
+
+def d_sdio_resp_spi(bits):
+    return "SDIO"
+
+
+def d_sdio_resp_sd(bits):
+    return "SDIO"
+
+
+decoders = [
+    #  0123456789abcdef -- maximum length is 16 characters
+    ( "LSB->MSB",		d_byte_lsb ),
+    ( "MSB->LSB",		d_byte_msb ),
+    ( "SDIO CMD",		d_sdio_cmd ),
+    ( "SDIO RESP (SD)",		d_sdio_resp_sd ),
+    ( "SDIO RESP (SPI)",	d_sdio_resp_spi ),
+]


Property changes on: developers/werner/ahrt/host/tmc/lib/decode.py
___________________________________________________________________
Name: svn:executable
   + *

Added: developers/werner/ahrt/host/tmc/lib/dplore.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/dplore.py	                        (rev 0)
+++ developers/werner/ahrt/host/tmc/lib/dplore.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -0,0 +1,584 @@
+#!/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()


Property changes on: developers/werner/ahrt/host/tmc/lib/dplore.py
___________________________________________________________________
Name: svn:executable
   + *

Modified: developers/werner/ahrt/host/tmc/lib/trigger.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/trigger.py	2008-09-16 00:08:57 UTC (rev 4645)
+++ developers/werner/ahrt/host/tmc/lib/trigger.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -80,6 +80,7 @@
 
     def __init__(self, **list):
 	trigger.__init__(self)
+	# @@@WARNING: Agilent use divs as the trigger level unit, not V.
 	self.level = setting(self, ":TRIG:"+self.mode+":LEV",
 	  lambda x: float(x),
 	  lambda x: "%.9f" % x)

Modified: developers/werner/ahrt/host/tmc/lib/wave.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/wave.py	2008-09-16 00:08:57 UTC (rev 4645)
+++ developers/werner/ahrt/host/tmc/lib/wave.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -361,16 +361,11 @@
 	if high < low:
 	    raise hell
 	res = digital()
-	state = None
 	for p in self.data:
 	    if p[1] >= high:
-		if state is None or not state:
-		    res.append(p[0], 1)
-		    state = True
+		res.append(p[0], 1)
 	    elif p[1] <= low:
-		if state is None or state:
-		    res.append(p[0], 0)
-		    state = False
+		res.append(p[0], 0)
 	return res
 
 
@@ -474,9 +469,9 @@
 	    return []
 	if len(self.data) == 1:
 	    if step is None:
-		return [self.initial]
-	    return map(lambda x: self.initial,
-	      range(0, int(round((self.t_end-self.data[0])/float(step)))))
+		return [int(self.initial)]
+	    return [int(self.initial)]* \
+	      int(round((self.t_end-self.data[0])/float(step)))
 	if step is None:
 	    step = self.sample_step()
 	value = int(self.initial)
@@ -486,9 +481,10 @@
 	    n = round((t-last)/step)
 	    if abs(n*step-(t-last)) > step*tolerance:
 		raise hell
-	    res.extend(map(lambda x: value, range(0, int(n))))
+	    res.extend([value]*int(n))
 	    last = t
 	    value = 1-value
+	res.extend([value]*int((self.end()-t)/step+0.5))
 	return res
 
     def __iter__(self):

Modified: developers/werner/ahrt/host/tmc/setup.py
===================================================================
--- developers/werner/ahrt/host/tmc/setup.py	2008-09-16 00:08:57 UTC (rev 4645)
+++ developers/werner/ahrt/host/tmc/setup.py	2008-09-16 00:11:17 UTC (rev 4646)
@@ -5,6 +5,7 @@
     description = "Test and Measurement Control",
     py_modules = [ "tmc.instrument",
 	"tmc.wave", "tmc.trigger", "tmc.shape",
+	"tmc.decode", "tmc.dplore",
         "tmc.meter", "tmc.scope", "tmc.power", "tmc.function" ],
     package_dir = { "tmc": "lib" },
     ext_modules = [




More information about the commitlog mailing list