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