Integrator windup

30.03.2010 21:08

I'm playing with a thyristor regulator proof-of-concept circuit for my lab power supply. Right now I'm using an op-amp integrator in the feedback control loop and I stumbled upon this nice example of how integrator windup can screw up a control system's response.

Simple feedback control loop

Image by en:User:Ap CC BY-SA 3.0

The upper, blue trace shows the output voltage (plant output y in the block diagram above). The lower, orange trace shows the integrator output (u). The orange shaded area shows where the plant is roughly linear - when integrator output is outside those bounds, the thyristor control circuit is in saturation and the output won't change much.

This is the response of the system to a negative step in the setpoint, without any special measures:

Negative step response with ringing due to integrator windup.

And this is the response to the same input with the integrator output limited by a Zener diode:

Negative step response with limited integrator output.

And finally, just to show off, here are both positive and negative step responses on the same picture:

Two step responses of a thyristor regulator.

I'm more interested in optimizing the positive response, since that's what will be important here. The output will be regulated by a linear regulator which can take a moment of excessive power dissipation due to overvoltage, but it can't correct for an input voltage that's too low.

The delay after the step response is 30 ms, while the theoretical minimum would be 10 ms. I doubt I'll be able to get it much lower without compromising stability.

Now I just have to figure out how this model will scale up to the real thing.

Posted by Tomaž | Categories: Analog | Comments »

Streaming gzip decompression in Python

24.03.2010 17:59

Python has a handy module called gzip that can be used to read gzip-compressed files just like if they were uncompressed. However, it has one nasty flaw - it needs support for the seek() method in file objects it reads, which isn't available for example in network streams.

Since the gzip command-line utility can decompress from a pipe, Python's requirement for seekable streams obviously isn't a requirement of the file format.

I hit upon this limitation when trying to implement a map reader function for compressed files for Disco map-reduce framework I'm experimenting with at work. It appears I'm not alone with this problem.

I nearly implemented a fix myself, when I found not one, but two patches gathering dust in the official Python's Bugzilla that solve this issue: 914340 and 1675951.

I tried the second one and it works beautifully. The description also claims that it improves read performance by 20%, although I can't confirm that.

This was submitted back in 2007 and as far as I see fits the description of a patch any free software maintainer should be most happy to accept. I can only guess why 3 years later this is still not in the official Python builds.

Posted by Tomaž | Categories: Code | Comments »

An electric anomaly

20.03.2010 19:26

Last week I got an email from Lucy Clarke asking me if I could explain how the following machine works and the readings she gets on the multimeter:

Diagram of the Lucy's machine

The coil is bifilar, meaning it has two winds of wire on it, one (heavier gauge) for taking power from DC and one (lighter gauge) going out to the multi-meter.
The coil also has a ferrite rod core so that, when the reed switch is open and there is no power, the magnets are attracted to the iron core, but when the reed-switch is closed and the power flows, it acts like an electromagnet and pushes away the magnet, so there is no 'sticky point'.
The peak reading on the multi-meter is 600V at 10.0A (...) When the rotor spins fast enough the voltage and the amps readings on the multimeter are off it's scale.

First, this is a variant of a brushless direct-current electric motor where the commutation is performed magnetically via the reed relay. A very inefficient one actually. For instance, it's only drawing power half of the time - ordinary two-pole DC motors will invert the direction of the current in the coil while it passes over the opposite magnetic pole, while this one merely switches the current off. There's also a huge air gap between the north and south poles of the permanent magnets reducing the strength of the magnetic field upon which the current in the coil can act.

If we forget about the moving parts for a moment, this device is very similar to an induction coil. Here's what an equivalent circuit looks like:

Model circuit diagram for Lucy's machine

On the primary side is a constant voltage source that gets periodically connected and disconnected to and from the primary side of a transformer. On the secondary side is a resistor that represents a model of the multimeter.

Here's how primary and secondary side voltage and current look like versus time for such a circuit in somewhat idealized conditions. The switch is closed in A and opened in B time interval:

Primary and secondary voltage and current versus time for Lucy's machine.

What is happening here? In time interval A the voltage of the source U1 appears on the secondary side and runs a current U1/R through the resistance of the multimeter. This current is also reflected on the primary side (the dotted line in i1(t) graph). However because of the self-inductance of the primary coil, the primary side current also has another component that is linearly increasing with time. So while the magnetic flux in the core of the coil due to U1/R on the primary side is compensated by the same current on the secondary side, the flux due to the self-inductance of the primary side is not (current difference I0 on the graph). This means that the magnetic flux in the core is steadily rising during interval A.

Magnetic field cannot collapse in an instance, so when the relay is switched off and primary side current is interrupted the only way it can be be sustained is by current on the secondary side. This effect causes the secondary side current to jump by I0, which then exponentially decays towards 0 as energy is lost in the resistance. This is also the principle of flyback converters.

Now we can explain the high voltage reading on the multimeter: when the multimeter is in voltage measurement mode, it has a high resistance between its probes (say 100 kΩ, but probably much higher). This means high R and a high R I0 voltage on the secondary side, since I0 current is forced through it by the collapsing magnetic field of the coil.

Since U1/R is negligible, the whole 0.5 A current budget appears as I0. 0.5 A times 100 kΩ gives a pretty respectable theoretical peak voltage. Of course, in practice this is much lower, but still high enough to affect the well-being of a multimeter.

On the other hand the high current readings are harder to explain. The theoretical peak current on the secondary side can't be higher than the peak on the primary side with this model. If the current limit on the power supply was working correctly, I can only offer some hand-waving explanations. One is that at higher speeds the machine gets in resonance with a tank capacitor in the power supply. So while the average current stayed below 0.5 A, peak current could have been well above that. The other is that some auto-ranging digital multimeters are very bad at measuring quickly changing values and may show completely wrong readings before they settle.

This analysis also ignores the effect of the permanent magnets moving in front of the coil. The effect of those is hard to judge, but my guess would be it is pretty insignificant due to the large air gap. For reference, I've included an idealized graph of the magnetic flux Φm through the core that is contributed by the magnets. If anything, the effect of this flux counteracts the self-induction of the coil and only causes the inducted voltages and currents in the circuit to be lower (which is logical, since the wheel is taking some energy from the system).

Posted by Tomaž | Categories: Analog | Comments »

Decoding IEEE 754

19.03.2010 13:20

There's a discussion going on today on Zemanta's staff mailing list about floating point formats and precision. I thought the following C code might also be useful to anyone trying to understand how floating point values are handled in modern hardware.

This manually decodes and prints parts of the floating point number stored in C's double type. Of course, it assumes that hardware uses the IEEE 754 "double" for that. That should be true at least on all descendants of the Intel i386 architecture.

#include <stdint.h>
#include <stdio.h>

int main() {
	double a = 0.1f;
	uint8_t* ac = (uint8_t*) &a;

	int n;
	for(n=7;n>=0;n--) printf("%02x ", ac[n]);
	printf("\n\n");

	int64_t e, m, s;

	m = 1;		// 1 (implied bit)
	m<<=4;
	m += ac[6] & ((1<<4)-1);	// 5
	m<<=8;
	m += ac[5];	// 13
	m<<=8;
	m += ac[4];	// 21
	m<<=8;
	m += ac[3];	// 29
	m<<=8;
	m += ac[2];	// 37
	m<<=8;
	m += ac[1];	// 45
	m<<=8;
	m += ac[0];	// 53

	printf("mantissa = %lld * 2^(-52)\n", m);

	e = ac[7] & ((1<<7)-1);	// 7
	e<<=4;
	e += ac[6]>>4;	// 11

	e -= 1023;	// exponent bias

	printf("exponent = %lld\n", e);

	s = ac[7]>>7;

	printf("sign bit = %lld\n\n", s);

	printf("%f ~= (-1)^%lld * %lld * 2^(%lld)\n", a, s, m, e - 52);

	return 0;
}

Adjust the value of a to taste. By default it will print:

3f b9 99 99 a0 00 00 00 

mantissa = 7205759511166976 * 2^(-52)
exponent = -4
sign bit = 0

0.100000 ~= (-1)^0 * 7205759511166976 * 2^(-56)

By the way, bc, the arbitrary precision calculator, comes very handy when doing calculations that involve limits of floating point formats.

Posted by Tomaž | Categories: Code | Comments »

i386 to amd64 without reinstall

14.03.2010 9:35

This is to confirm that it's indeed possible to do the impossible - to upgrade an existing Debian Lenny i386 installation to amd64 in-place. I did it by roughly following the instructions in this document by Robin Lee Powell. Judging by the package versions mentioned there, I assume it has been written for some older version of Debian, but the basic steps still worked for me on Lenny.

At the same time I also confirm that all warnings and curse words in that document are there for a very good reason.

On installing the amd64 version of libc6 over i386 one the system ended up in a very broken state and I ended up picking together symlinks and ldconfig cache by hand from a static busybox shell. I'm thinking the breakage was because I had the optimized libc6-i686 installed, but I'm not sure - in the end there were something like 4 different binaries the /lib/libc.so.6 link could point to. Here I should mention that the default busybox package in Debian isn't statically built - that's in a separate package called busybox-static.

libc6 upgrade also broke hostname resolution, which I fixed after much digging around by changing the hosts: line in /etc/nsswitch.conf to:

hosts: files dns

It's also worth mentioning that until the very end of the procedure, it was impossible to get a new console login.

After I resolved that, it was pretty smooth sailing. I used aptitude instead of apt-get for the final step, which was much less painful. The only interesting thing was that the old i386 packages in apitude appeared as uninstalled, while dpkg showed them as installed. This caused some problems when installing conflicting packages, but nothing that couldn't be resolved by manually running dpkg.

Oh and instead of downloading those basic amd64 packages and all their dependencies by hand with a web browser, I simply downloaded a small CD ISO and kept it mounted somewhere, which proved very handy.

In conclusion, while this is possible, it's more akin to Linux from Scratch than something you would want to do regularly. I'm pretty certain that if the same situation arises again, I will choose to do a clean reinstall. But it was a fun exercise in Debian system administration and the experience might help the next time I'm on a system where 'ls' segfaults.

Posted by Tomaž | Categories: Code | Comments »

Bogofilter and word histogram

12.03.2010 21:25

I receive approximately 104 spam messages per month to my personal email address (compare this to around 3000 in September 2007).

I've long ago abandoned all hope that I can hide the address itself from spammers and their crawlers by playing tricks with obfuscation and Turing tests. Now you can find it in clear on numerous sites. I'm still convinced that it's not worth it and I wouldn't turn back to obfuscation even if I started using a fresh address. It's a far too fragile defense. All it takes is a single breach - one web site not hiding the address well enough (you can't control them all!), one person with a spyware infested computer with your address in the address book - and most of the effort has been for nothing.

These days on average 5 spams per day will get through my more or less default Bogofilter setup. I don't know how many legitimate mails end up in the spam folder - it's impossible to check them all manually. Every once in a while I check a few tens of mails classified as spam that are least likely to be spam according to Bogofilter scoring. So far I have only seen a handful (less than 10) useful mails end up there and that was enough to keep me convinced that the false-positive rate is negligible.

I run the Bogofilter in constant learning mode and the database I'm currently using is now a little more than 2.5 years old (I think the previous one got corrupted in a power outage). While tuning some classification parameters I found that it has this peculiar characteristic:

$ bogoutil -H ./wordlist.db
Histogram
score   count  pct  histogram
0.00   518443 19.51 ############
0.05     3923  0.15 #
0.10     5205  0.20 #
0.15     1910  0.07 #
0.20     1418  0.05 #
0.25     5231  0.20 #
0.30     1753  0.07 #
0.35     1069  0.04 #
0.40     2573  0.10 #
0.45     1113  0.04 #
0.50     2070  0.08 #
0.55     1509  0.06 #
0.60     1422  0.05 #
0.65     1316  0.05 #
0.70     1405  0.05 #
0.75     1327  0.05 #
0.80     1284  0.05 #
0.85     1346  0.05 #
0.90     1621  0.06 #
0.95  2101188 79.08 ################################################
tot   2657126
hapaxes:  ham  318147 (11.97%), spam 1679040 (63.19%)
   pure:  ham  511489 (19.25%), spam 2099448 (79.01%)

I'm not sure how such databases dwelling in other corners of the internet look like. This histogram means that my legitimate mails have a very distinct vocabulary with words that almost never appear in spam. There are relatively few words that appear in both classes (only 1.7% out of 2.5 million!). I was expecting a much more continuous distribution.

I'm thinking some of this is probably due to a part of my mail being in Slovene (and Slovenian spam is thankfully almost nonexistent). But still not enough I think to justify such a result.

On the other hand, considering the excellent success rate of filtering, I should have expected an outcome like this.

Posted by Tomaž | Categories: Code | Comments »

1n41 break 48

03.03.2010 21:37

1n4148 diode broken in half

This is what was left of a 1n4148 diode after an aluminum 47 μF electrolytic capacitor was repeatedly charged through it from a low-impedance source. Average power dissipation was well below its specified maximum.

Signal diodes don't survive peak currents much larger than their continuous current rating.

Posted by Tomaž | Categories: Life | Comments »

Doesn't do what I want

01.03.2010 19:34

Here's another weird Perl quirk that has a potential to cause error messages in scripts which lead you into a completely wrong direction.

$ mkdir foo
$ perl -le 'print open(F, "<foo");'
1

To cite Perl documentation, "Open returns nonzero upon success". Obviously, this means that Perl thinks the open() call above succeeded. However the filehandle F is useless - all it ever does is return undefs.

So I guess this means that before every call to open() you should check if the argument accidentally points to a directory, so you can give a meaningful error message. Otherwise you might read a bunch of undefs from it without noticing.

Posted by Tomaž | Categories: Code | Comments »