--- Begin Message ---
Author: werner
Date: 2008-09-29 02:33:55 +0200 (Mon, 29 Sep 2008)
New Revision: 4679
Added:
developers/werner/ahrt/host/tmc/demo/eye.py
developers/werner/ahrt/host/tmc/lib/phosphor.py
Modified:
developers/werner/ahrt/host/tmc/lib/trigger.py
developers/werner/ahrt/host/tmc/lib/wave.py
Log:
Highlights:
- new module phosphor.py to view how often patterns repeat
- example eye.py to plot an eye diagram with phosphor.py
Details:
- lib/wave.py (analog, digital): moved binary search by time from "get_one"
into new method "index"
- lib/trigger.py (edge.apply): returned a false trigger at the start of the
recording when using slope.Both
- lib/phosphor.py: "phosphor" screen that remembers how often a pixel was drawn
- demo/eye.py: example for drawing an eye diagram with phosphor.py
Added: developers/werner/ahrt/host/tmc/demo/eye.py
===================================================================
--- developers/werner/ahrt/host/tmc/demo/eye.py (rev 0)
+++ developers/werner/ahrt/host/tmc/demo/eye.py 2008-09-29 00:33:55 UTC (rev
4679)
@@ -0,0 +1,156 @@
+#!/usr/bin/python
+
+#
+# @@@ WORK IN PROGRESS.
+#
+# What's missing:
+#
+# - there's no way to display the color bar for reference
+# - there's no easy way to add axis and ticks
+# - the data processing to produce the eye diagram is complex enough that it
+# deserves to be packaged as one high-level operation in the library
+#
+
+import time
+from tmc.scope import *
+from tmc.trigger import *
+from tmc.phosphor import phosphor
+from math import ceil, floor
+
+#
+# This experiment captures a signal and draws an eye diagram. The eye diagram
+# allows examination of signal integrity and timing violations.
+#
+# Note that this is still a bit primitive. A proper eye diagram would also show
+# the "forbidden" areas.
+#
+# Our test setup is a function generator connected to the channel 2 ("B") of a
+# Rigol DS1000. The function generator emits a 1MHz square wave with slowly
+# (100ns) rising and falling edges.
+#
+# The parameters below:
+#
+# width: the nominal width of a pulse
+# overlap: the time we capture before and after each pulse
+# waveforms: how many waveforms we include in the output
+#
+
+width = 0.5e-6
+overlap = 0.2e-6
+waveforms = 1000
+
+# Open the scope
+
+s = rigol_ds1000c()
+s.debug = False
+
+# Set the horizontal system to have the trigger at the center and the scale
+# set to 10ns/div. We pick this scale because it gives us a 200MSa/s
+# resolution.
+
+s.hor.pos = 0
+s.hor.scale = 10e-9
+#s.hor.scale = 100e-6
+
+# We capture channel "B". Our input has a peak-to-peak voltage of 3.3V, so we
+# set the vertical system such that the screen center is at 1.5V and the scale
+# is 1V/div.
+
+ch = s.ch[1]
+ch.pos = 1.5
+ch.scale = 1
+
+# Define an edge trigger that triggers on the rising edge at 1.65V.
+
+t = edge(slope = slope.Rising, level = 1.65)
+
+# Apply the trigger to channel "B".
+
+t.source(ch)
+
+# Set the horizontal system to single sweep, stop the scope, then start it.
+
+s.hor.sweep = sweep.Single
+s.hor.stop()
+s.hor.run()
+
+# Wait until the scope has triggered and finished the acquisition.
+
+while s.hor.state() != state.Stop:
+ time.sleep(0.1)
+
+# With the above settings, the scope captures about 1.3ms left and right of the
+# trigger. We extract 1ms of the captured waveform.
+
+w = s.wave(ch, start = 0, end = width*waveforms)
+
+# Digitize the waveform and find the rising and falling edges. We construct the
+# trigger to extract the waveforms on the fly.
+
+d = w.digitize(0.8, 2.0)
+e = edge(slope = slope.Both, level = 0.5).apply(d)
+
+# Print what we got.
+
+print len(w), "samples,", len(e), "edges"
+
+# The "phosphor" display operates on a discrete pixel matrix. We therefore
+# obtain a list of the sample values, stripping the time.
+
+samples = map(lambda p: p[1], w)
+
+# Autoscale the y-axis: vcenter is the value at the center of the display. vres
+# is the peak to peak maximum. yres is the arbitrary y resolution we choose.
+# (260 is slightly larger than 256, the typical ADC resolution, so we should
+# get the full dynamic range of the ADC.)
+
+low = min(samples)
+high = max(samples)
+
+vcenter = (high+low)/2
+vres = high-low
+yres = 260
+
+# Autoscale the x-axis: tres is the time interval we want to see. 1/sr is the
+# sample rate. xres is the number of horizontal pixels we give to have each
+# sample its own column.
+
+tres = width+2*overlap
+sr = w.data[1][0]-w.data[0][0]
+xres = tres/sr
+
+# Create a phosphor "screen" with the desired resolution. We add some extra
+# pixels compensate for rounding errors.
+
+ph = phosphor(int(ceil(xres)), yres+2)
+
+# Calculate at which x-offset we put the trigger point.
+
+offset = int(overlap/sr)
+
+# Convert the samples to the y-coordinates on the "screen".
+
+values = map(lambda s: int(round((s-vcenter)/vres*yres+yres/2)), samples)
+
+# Convert all triggers from time to sample indices.
+
+starts = map(lambda t: w.index(t), e)
+
+# Draw the waveform in the window around each trigger index.
+
+for s in starts:
+ ph.draw_window(values, s, offset)
+
+# Generate a PNM image of the screen with the following characteristics:
+#
+# - each screen pixel is represented by a 2x1 rectangle in the image
+# - the frequency is normalized on a logarithmic scale
+# - the frequency of events is indicated with a color spectrum
+
+s = ph.pnm(2, 1, normalize = ph.logarithmic, colors = ph.color_spectrum)
+
+# Write the image to a file called "out.pnm".
+
+f = open("out.pnm", "w")
+print >>f, s
+f.close()
Property changes on: developers/werner/ahrt/host/tmc/demo/eye.py
___________________________________________________________________
Name: svn:executable
+ *
Added: developers/werner/ahrt/host/tmc/lib/phosphor.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/phosphor.py
(rev 0)
+++ developers/werner/ahrt/host/tmc/lib/phosphor.py 2008-09-29 00:33:55 UTC
(rev 4679)
@@ -0,0 +1,182 @@
+#
+# phosphor.py - "Phosphor" screen that remembers how often a pixel was drawn
+#
+# Copyright (C) 2008 by OpenMoko, Inc.
+# Written by Werner Almesberger <[EMAIL PROTECTED]>
+# 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.
+#
+
+# @@@ EXPERIMENTAL !
+
+#
+# The screen width is in samples. Lines can only go from one sample to the
+# next, i.e., from x to x+1. Lines don't include their final point, the
+# assumption being that this point will be part of the next line drawn.
+#
+# There are several line algorithms:
+#
+# - "saturated" adds one to each pixel visited
+#
+# - "beam" mimicks the beam on an oscilloscope's CRT by decreasing the
+# intensity linearly with the length of the line, such that the sum of all
+# the pixels drawn for that line is one.
+#
+# "saturated" produces good-looking images, but makes areas with a high noise
+# look more intense than they are. "beam" is more accurate, but has the small
+# disadvantage that a lone outlier does not just use a single color.
+#
+# Colors are encoded as RGB tuples, where each component is in the range 0 to
+# 1. The color conversion function is given the value of the pixel, normalized
+# into the range 0 to 1.
+#
+# The following conversion functions are available:
+#
+# - "linear": this is just a linear greyscale from black to white
+#
+# - "default": similar to "linear", but the minimum value is 10% and a mild
+# fake gamma correction is applied, so that infrequent events are more
+# visible
+#
+# - "spectrum": uses a simple color spectrum going from blue to green to red
+#
+# Finally also the normalization is configurable:
+#
+# - linear: linear mapping of the range [0, max] to [0, 1]
+#
+# - logarithmic: logarithmic mapping of the range [ln(min), ln(max)] to [0, 1].
+# Black pixels are excluded from the minimum.
+#
+
+
+from math import log
+
+
+class norm_linear:
+
+ def __init__(self, min, max):
+ self.f = 1.0/max
+
+ def norm(self, v):
+ return self.f*v
+
+
+class norm_logarithmic:
+
+ def __init__(self, min, max):
+ self.offset = -log(min)
+ self.f = 1.0/(log(max)+self.offset)
+
+ def norm(self, v):
+ return self.f*(log(v)+self.offset)
+
+
+class phosphor(object):
+
+ linear = norm_linear
+ logarithmic = norm_logarithmic
+
+ def __init__(self, x, y):
+ self.m = []
+ for i in range(0, x):
+ self.m.append([0.0]*y)
+ self.x = x
+ self.y = y
+
+ def saturated_line(self, x, y0, y1):
+ if y0 == y1:
+ self.m[x][y0] += 1.0
+ elif y0 < y1:
+ for i in range(0, y1-y0):
+ self.m[x][y0+i] += 1.0
+ else:
+ for i in range(0, y0-y1):
+ self.m[x][y0-i] += 1.0
+
+ def beam_line(self, x, y0, y1):
+ if y0 == y1:
+ self.m[x][y0] += 1.0
+ elif y0 < y1:
+ f = 1.0/(y1-y0)
+ for i in range(0, y1-y0):
+ self.m[x][y0+i] += f
+ else:
+ f = 1.0/(y0-y1)
+ for i in range(0, y0-y1):
+ self.m[x][y0-i] += f
+
+ draw_line = beam_line
+
+ #
+ # "draw_window" is where really all the magic happens. It differs from a
+ # simple iteration over a subset of the samples by allowing the reference
+ # point of the waveform and the reference point on the screen to bet set
+ # independently.
+ #
+ # "samples" is the list of sample values.
+ # "start" is the reference point in the sample list.
+ # "offset" is the on-screen position of the reference point. If "offset" is
+ # positive, samples before "start" are drawn.
+ #
+
+ def draw_window(self, samples, start = 0, offset = 0):
+ for x in range(max(0, offset-start),
+ min(self.x, len(samples)-start+offset)-1):
+ pos = start+x-offset
+ self.draw_line(x, samples[pos], samples[pos+1])
+
+ def color_default(self, v):
+ return [0.1+0.9*v*(2.0-v)]*3
+
+ def color_linear(self, v):
+ return (v, v, v)
+
+ def color_spectrum(self, v):
+ return (
+ max(0.0, (4.0*v-4.0)*(1.0-v)+1.0),
+ max(0.0, 8.0*v*(1.0-v)-1.0),
+ max(0.0, 4.0*v*-v+1.0))
+ return (
+ min(1.0, max(0.0, (2.0*v-2.0)*(v+0.5)+1.0)),
+ max(0.0, 16.0*v*(1.0-v)-1.0),
+ min(1.0, 8.0*v*v))
+
+ def max(self):
+ return reduce(lambda a, b: max(a, max(b)), self.m, 0)
+
+ def min(self):
+ return reduce(lambda a, b: min(a,
+ reduce(lambda a, b: [a, min(a, b)][b != 0], b, 1)),
+ self.m, 1)
+
+ def pnm(self, px = 1, py = 1, colors = None, normalize = norm_linear):
+ if colors is None:
+ fn = self.color_default
+ elif isinstance(colors, list):
+ fn = lambda v: colors[min(len(colors)-1, int(v*len(colors)))]
+ else:
+ fn = colors
+
+ s = "P6 %d %d 255\n" % (self.x*px, self.y*py)
+
+ norm = normalize(self.min(), self.max())
+
+ for y in range(self.y-1, -1, -1):
+ row = ""
+ for x in range(0, self.x):
+ if self.m[x][y] == 0:
+ t = (0, 0, 0)
+ else:
+ t = fn(norm.norm(self.m[x][y]))
+ t = chr(min(int(t[0]*256), 255))+ \
+ chr(min(int(t[1]*256), 255))+ \
+ chr(min(int(t[2]*256), 255))
+ for i in range(0, px):
+ row += t
+ for i in range(0, py):
+ s += row
+ return s
Property changes on: developers/werner/ahrt/host/tmc/lib/phosphor.py
___________________________________________________________________
Name: svn:executable
+ *
Modified: developers/werner/ahrt/host/tmc/lib/trigger.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/trigger.py 2008-09-28 02:12:36 UTC
(rev 4678)
+++ developers/werner/ahrt/host/tmc/lib/trigger.py 2008-09-29 00:33:55 UTC
(rev 4679)
@@ -109,7 +109,7 @@
if self.level <= 0 or self.level >= 1:
return []
if self.slope == slope.Both:
- return wave.data[:]
+ return wave.data[1:]
res = []
i = int(wave.initial ^ self.slope == slope.Falling)+1
while i < len(wave.data):
Modified: developers/werner/ahrt/host/tmc/lib/wave.py
===================================================================
--- developers/werner/ahrt/host/tmc/lib/wave.py 2008-09-28 02:12:36 UTC (rev
4678)
+++ developers/werner/ahrt/host/tmc/lib/wave.py 2008-09-29 00:33:55 UTC (rev
4679)
@@ -344,11 +344,14 @@
raise hell
self.data.extend(wave.data)
- def get_one(self, t):
+ def index(self, t):
if len(self.data) == 0 or t < self.data[0][0] or t > self.data[-1][0]:
raise hell
- return self.data[binary(self.data, lambda x: x[0], t)]
+ return binary(self.data, lambda x: x[0], t)
+ def get_one(self, t):
+ return self.data[self.index(t)]
+
def __iter__(self):
return analog_iter(self)
@@ -434,11 +437,15 @@
self.data.extend(wave.data[1:])
self.t_end = wave.t_end
+ def index(self, t):
+ if len(self.data) == 0 or t < self.data[0] or t > self.t_end:
+ raise hell
+ return binary(self.data, lambda x: x, t)
+
def get_one(self, t):
if len(self.data) == 0 or t < self.data[0] or t > self.t_end:
raise hell
- return int(self.initial ^
- (binary(self.data, lambda x: x, t) & 1))
+ return int(self.initial ^ (self.index(t) & 1))
# experimental
--- End Message ---
--- Begin Message ---
Author: werner
Date: 2008-09-29 08:07:38 +0200 (Mon, 29 Sep 2008)
New Revision: 4680
Added:
developers/werner/neodog/
developers/werner/neodog/Makefile
developers/werner/neodog/event.c
developers/werner/neodog/neodog.c
developers/werner/neodog/neodog.h
developers/werner/neodog/pmu.c
Log:
Tough watchdog daemon.
Added: developers/werner/neodog/Makefile
===================================================================
--- developers/werner/neodog/Makefile (rev 0)
+++ developers/werner/neodog/Makefile 2008-09-29 06:07:38 UTC (rev 4680)
@@ -0,0 +1,25 @@
+CC=arm-angstrom-linux-gnueabi-gcc
+
+CFLAGS=-Wall -Wshadow -g -O
+
+OBJS=neodog.o event.o pmu.o
+
+.PHONY: all clean depend spotless
+
+all: neodog
+
+neodog: $(OBJS)
+
+depend:
+ $(CPP) $(CFLAGS) -MM -MG *.c >.depend || \
+ { rm -f .depend; exit 1; }
+
+ifeq (.depend,$(wildcard .depend))
+include .depend
+endif
+
+clean:
+ rm -f $(OBJS) .depend
+
+spotless: clean
+ rm -f neodog
Added: developers/werner/neodog/event.c
===================================================================
--- developers/werner/neodog/event.c (rev 0)
+++ developers/werner/neodog/event.c 2008-09-29 06:07:38 UTC (rev 4680)
@@ -0,0 +1,58 @@
+/*
+ * event.c - Direct I2C polling in case events are lost
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <[EMAIL PROTECTED]>
+ * 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.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include <linux/input.h>
+
+#include "neodog.h"
+
+
+#define EVENT_DEVICE "/dev/input/event2"
+
+
+int process_event(int fd)
+{
+ struct input_event ev;
+ ssize_t got;
+
+ got = read(fd, &ev, sizeof(ev));
+ if (got < 0) {
+ perror("read");
+ return 0;
+ }
+ if (got != sizeof(ev))
+ return 0;
+ if (ev.type != EV_KEY)
+ return 0;
+ if (ev.code != KEY_POWER)
+ return 0;
+ return ev.value ? 1 : -1;
+}
+
+
+int open_event(void)
+{
+ int fd;
+
+ fd = open(EVENT_DEVICE, O_RDONLY);
+ if (fd < 0) {
+ perror(EVENT_DEVICE);
+ exit(1);
+ }
+ return fd;
+}
Added: developers/werner/neodog/neodog.c
===================================================================
--- developers/werner/neodog/neodog.c (rev 0)
+++ developers/werner/neodog/neodog.c 2008-09-29 06:07:38 UTC (rev 4680)
@@ -0,0 +1,133 @@
+/*
+ * neodog.c - Tough watchdog daemon
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <[EMAIL PROTECTED]>
+ * 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.
+ */
+
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+#include <sched.h>
+#include <poll.h>
+#include <sys/mman.h>
+
+#include <linux/unistd.h>
+#include <linux/reboot.h>
+
+#include "neodog.h"
+
+
+#define TIMEOUT 8 /* power down if ONKEY is held for 8 seconds */
+
+
+static void invulnerabilize(void)
+{
+ struct sched_param prm;
+
+ if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0) {
+ perror("mlockall");
+ exit(1);
+ }
+ prm.sched_priority = sched_get_priority_max(SCHED_FIFO);
+ if (prm.sched_priority < 0) {
+ perror("sched_get_priority_max SCHED_FIFO");
+ exit(1);
+ }
+ if (sched_setscheduler(0,SCHED_FIFO,&prm) < 0) {
+ perror("sched_setscheduler SCHED_FIFO");
+ exit(1);
+ }
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ signal(SIGUSR1, SIG_IGN);
+ signal(SIGUSR2, SIG_IGN);
+}
+
+
+static void power_off(int sig)
+{
+#ifdef DEBUG
+ fprintf(stderr, "***ALARM***\n");
+ return;
+#endif
+ syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_POWER_OFF);
+}
+
+
+static void set_alarm(int on)
+{
+ static int is_on = 0;
+
+#ifdef DEBUG
+ fprintf(stderr, "set_alarm(%d)\n", on);
+#endif
+ if (on == is_on)
+ return;
+ is_on = on;
+ if (on)
+ alarm(TIMEOUT);
+ else
+ alarm(0);
+}
+
+
+int main(void)
+{
+ struct pollfd event = {
+ .fd = open_event(),
+ .events = POLLIN,
+ };
+ time_t next = 0;
+ int pmu;
+
+ /*
+ * It's a bit overkill to listen to events and to pull the PMU.
+ * The events give us better response granularity, but we also need to
+ * poll in case something overruns the event buffer.
+ */
+ pmu = open_pmu();
+ invulnerabilize();
+ signal(SIGALRM, power_off);
+ while (1) {
+ int fds;
+ time_t now;
+
+ fds = poll(&event, 1, 1000);
+ if (fds < 0) {
+ perror("poll");
+ return 1;
+ }
+ time(&now);
+ if (fds) {
+ switch (process_event(event.fd)) {
+ case 0:
+ break;
+ case 1:
+ set_alarm(0);
+ break;
+ case -1:
+ set_alarm(0);
+ break;
+ default:
+ abort();
+ }
+ }
+ if (!fds || now >= next) {
+ set_alarm(get_onkey(pmu));
+ next = now+1;
+ }
+ }
+}
Added: developers/werner/neodog/neodog.h
===================================================================
--- developers/werner/neodog/neodog.h (rev 0)
+++ developers/werner/neodog/neodog.h 2008-09-29 06:07:38 UTC (rev 4680)
@@ -0,0 +1,23 @@
+/*
+ * neodog.h - Tough watchdog daemon
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <[EMAIL PROTECTED]>
+ * 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.
+ */
+
+#ifndef NEODOG_H
+#define NEODOG_H
+
+int get_onkey(int fd);
+int open_pmu(void);
+
+int process_event(int fd);
+int open_event(void);
+
+#endif /* !NEODOG_H */
Added: developers/werner/neodog/pmu.c
===================================================================
--- developers/werner/neodog/pmu.c (rev 0)
+++ developers/werner/neodog/pmu.c 2008-09-29 06:07:38 UTC (rev 4680)
@@ -0,0 +1,65 @@
+/*
+ * pmu.c - Direct I2C polling in case events are lost
+ *
+ * Copyright (C) 2008 by OpenMoko, Inc.
+ * Written by Werner Almesberger <[EMAIL PROTECTED]>
+ * 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.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include "neodog.h"
+
+
+#define I2C_DEVICE "/dev/i2c-0"
+#define I2C_ADDRESS 0x73
+
+#define OOCSTAT 0x12
+#define OOCSTAT_ONKEY 1
+
+
+int get_onkey(int fd)
+{
+ uint8_t oocstat;
+ struct i2c_smbus_ioctl_data msg = {
+ .read_write = I2C_SMBUS_READ,
+ .command = OOCSTAT,
+ .size = I2C_SMBUS_BYTE_DATA,
+ .data = (void *) &oocstat,
+ };
+
+ if (ioctl(fd, I2C_SMBUS, (void *) &msg) < 0) {
+ perror("ioctl(I2C_RDWR)");
+ exit(1);
+ }
+ return !(oocstat & OOCSTAT_ONKEY);
+}
+
+
+int open_pmu(void)
+{
+ int fd;
+
+ fd = open(I2C_DEVICE, O_RDWR);
+ if (fd < 0) {
+ perror(I2C_DEVICE);
+ exit(1);
+ }
+ if (ioctl(fd, I2C_SLAVE_FORCE, I2C_ADDRESS) < 0) {
+ perror("ioctl(I2C_SLAVE_FORCE)");
+ exit(1);
+ }
+ return fd;
+}
--- End Message ---