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

werner at docs.openmoko.org werner at docs.openmoko.org
Sat Sep 13 04:45:32 CEST 2008


Author: werner
Date: 2008-09-13 04:45:31 +0200 (Sat, 13 Sep 2008)
New Revision: 4642

Added:
   developers/werner/ahrt/host/tmc/demo/dplore.py
   developers/werner/ahrt/host/tmc/etc/
   developers/werner/ahrt/host/tmc/etc/40-usbtmc-permissions.rules
Modified:
   developers/werner/ahrt/host/tmc/Makefile
   developers/werner/ahrt/host/tmc/README
   developers/werner/ahrt/host/tmc/lib/wave.py
Log:
Highlights:
- also non-root users can access USB TMC devices
- prototype of an interactive analyzer for digital signals

Details:
- etc/40-usbtmc-permissions.rules: udev rules to make all USB TMC devices
  belong to group "usbtmc" with group write access
- Makefile: new target "setup-system" to prepare the system for the use of
  the "usbtmc" group (tested with Ubuntu 8.04)
- README: documented the new system setup step
- lib/wave.py (digital.sample_step): determine the most likely sample step size   in a digital wave
- lib/wave.py (digital.sample): sample a digital wave and return a list of the
  bits
- demo/dplore.py: interactive explorer for digital signals



Modified: developers/werner/ahrt/host/tmc/Makefile
===================================================================
--- developers/werner/ahrt/host/tmc/Makefile	2008-09-12 11:51:20 UTC (rev 4641)
+++ developers/werner/ahrt/host/tmc/Makefile	2008-09-13 02:45:31 UTC (rev 4642)
@@ -1,4 +1,4 @@
-.PHONY:		all install clean spotless
+.PHONY:		all install system-setup clean spotless
 
 all:		tmc.so
 
@@ -9,6 +9,15 @@
 install:
 		python setup.py install
 
+# Should perhaps use Debian's addgroup, which gives us better control over the
+# GID selected, but that one is lacking the rather convenient -f option.
+
+system-setup:
+		groupadd -f usbtmc
+		install -m 0644 etc/40-usbtmc-permissions.rules \
+		  /etc/udev/rules.d
+		/etc/init.d/udev restart
+
 clean:
 
 spotless:

Modified: developers/werner/ahrt/host/tmc/README
===================================================================
--- developers/werner/ahrt/host/tmc/README	2008-09-12 11:51:20 UTC (rev 4641)
+++ developers/werner/ahrt/host/tmc/README	2008-09-13 02:45:31 UTC (rev 4642)
@@ -40,6 +40,32 @@
 annotated examples in the demo/ directory.
 
 
+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.
+
+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
+using udev.
+
+The following command creates the "usbtmc" group, adds the permission
+information for USB TMC devices supported by TMC, and restarts udevd:
+
+# make system-setup
+
+To give users membership of the "usbtmc" group, run the following
+command for each user:
+
+# adduser username usbtmc 
+
+After logging in (or requivalent), this user should then be able to
+access USB TMC devices.
+
+
 Low-level interface
 ===================
 

Added: developers/werner/ahrt/host/tmc/demo/dplore.py
===================================================================
--- developers/werner/ahrt/host/tmc/demo/dplore.py	                        (rev 0)
+++ developers/werner/ahrt/host/tmc/demo/dplore.py	2008-09-13 02:45:31 UTC (rev 4642)
@@ -0,0 +1,406 @@
+#!/usr/bin/python
+
+#
+# WORK IN PROGRESS !
+#
+# TODO:
+# - zoom
+# - pan
+# - resize
+# - auto-scale
+# - turn this into a module
+# - clean up
+#
+
+# apt-get install python-tk
+
+from Tkinter import *
+from tmc.wave import waves
+import sys
+
+
+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):
+    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()
+    if step is not None and step < min:
+	min = step
+d = []
+for wv in w[1:]:
+    a = wv.digitize(0.5).sample(min)
+#    print len(a)
+    if len(a) >= 1500:
+	d.append(a[0:1500])
+dplore(d.reverse())
+
+# 1, 2, 3, 5, 10


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

Added: developers/werner/ahrt/host/tmc/etc/40-usbtmc-permissions.rules
===================================================================
--- developers/werner/ahrt/host/tmc/etc/40-usbtmc-permissions.rules	                        (rev 0)
+++ developers/werner/ahrt/host/tmc/etc/40-usbtmc-permissions.rules	2008-09-13 02:45:31 UTC (rev 4642)
@@ -0,0 +1,35 @@
+#
+# udev permissions for USB TMC devices
+#
+
+#
+# The generic way for identifying USB TMC devices would be by first checking
+# that bDeviceClass is either 0 (as presribed by USB TMC) or 0xff (as
+# implemented by Rigol) and that bDeviceSubClass and bDeviceProtocol are both
+# 0xff, and then diving into the interface descriptors and identifying that
+# there is one with bInterfaceClass 0xfe (application specific) and
+# bInterfaceSubClass 0x03 (USB TMC).
+#
+# Unfortunately, udev's mechanism of finding this information via usb_id
+# (which, oddly enough, seems to be a bit redundant since udev can also see
+# the interface itself in some cases), appears to be broken.
+#
+# We thus fall back to using a list of vendor/product IDs for now :-(
+#
+
+SUBSYSTEM=="usb_device", GOTO="usb_tmc_start"
+SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", GOTO="usb_tmc_start"
+GOTO="usb_tmc_end"
+
+LABEL="usb_tmc_start"
+
+# Rigol DS1000 series oscilloscopes
+ATTRS{idVendor}=="0400", ATTRS{idProduct}=="05dc", MODE="0660", GROUP="usbtmc"
+
+# Picotest M3500A multimeter
+ATTRS{idVendor}=="164e", ATTRS{idProduct}=="0dad", MODE="0660", GROUP="usbtmc"
+
+# Picotest G5100A function generator
+ATTRS{idVendor}=="164e", ATTRS{idProduct}=="13ec", MODE="0660", GROUP="usbtmc"
+
+LABEL="usb_tmc_end"

Modified: developers/werner/ahrt/host/tmc/lib/wave.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/wave.py	2008-09-12 11:51:20 UTC (rev 4641)
+++ developers/werner/ahrt/host/tmc/lib/wave.py	2008-09-13 02:45:31 UTC (rev 4642)
@@ -445,6 +445,52 @@
 	return int(self.initial ^
 	  (binary(self.data, lambda x: x, t) & 1))
 
+    # experimental
+
+    def sample_step(self, tolerance = 0.01):
+	if len(self.data) < 2:
+	    return None
+	min = self.data[1]-self.data[0]
+	for i in range(1, len(self.data)-1):
+	    if min < self.data[i+1]-self.data[i]:
+		min = self.data[i+1]-self.data[i]
+	div = 1
+	while True:
+	    step = min/float(div)
+	    fuzz = step*tolerance
+	    for t in self.data:
+		d = t-self.data[0]
+		n = round(d/step)
+		if abs(d-n*step) > fuzz:
+		    break
+	    else:
+		return step
+	    div += 1
+
+    # experimental
+
+    def sample(self, step = None, tolerance = 0.01):
+	if len(self.data) == 0:
+	    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)))))
+	if step is None:
+	    step = self.sample_step()
+	value = int(self.initial)
+	last = self.data[0]
+	res = []
+	for t in self.data[1:]:
+	    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))))
+	    last = t
+	    value = 1-value
+	return res
+
     def __iter__(self):
 	return digital_iter(self)
 




More information about the commitlog mailing list