Gas station without pumps

2015 July 25

Noise from PteroDAQ KL25Z

Filed under: Circuits course,Data acquisition — gasstationwithoutpumps @ 15:31
Tags: , , , , , ,

In a series of posts (most recently More on measuring PteroDAQ KL25Z input impedance), I’ve been measuring the input impedance of my various ways of measuring AC voltage, and having some trouble getting a reasonable value for using the PteroDAQ as a measuring device.  In the most recent post, I noted that hardware averaging seemed to make the measurements worse, not better, when the input impedance was high.

I decided to map out how bad this measurement error was, by changing the source resistance and seeing how the voltage measurement changed. I picked a low frequency (55Hz) with a high sampling rate (6001.5Hz), so that aliasing was not an issue.

The voltage measurements are fine up to a source impedance of 10kΩ without averaging, but only to 1kΩ with 32× hardware averaging.

The voltage measurements are fine up to a source impedance of 10kΩ without averaging, but only to 1kΩ with 32× hardware averaging.

My conjecture about the problem with the 32× averaging was that the KL25Z sample-and-hold circuitry was injecting interference onto the pin, and that too high a source impedance did not provide sufficient current to eliminate this noise before the next sampling of the pin.

I tried fixing the problem by adding a small capacitor between the pin being measured (PTE0) and ground. The idea is that the capacitor can short out the high-frequency interference, using charge from the capacitor to cancel the noise rather than from the source. If the capacitor is too large, then the low-pass RC filter of the source impedance and the capacitance will reduce the signal, but if it is too small, then the sample-and-hold will be confused by the noise from the previous sample. With the 55Hz signal and a 100kΩ source impedance, I tried a number of capacitors, looking for the one that would maximize the voltage reading with 32× hardware averaging. I settled on 470pF, which would give a corner frequency of 3.39kHz (approximately my Nyquist frequency).

Putting in a 470pF capacitor to bypass the noise from the sampling helps when there is no averaging, but not so much when there is averaging.

Putting in a 470pF capacitor to bypass the noise from the sampling helps when there is no averaging, but not so much when there is averaging.

With the 470pF capacitor, the source resistance can get as high as 100kΩ before the noise injection becomes a problem, when not using hardware averaging (at the 6kHz sampling rate—higher sampling rates would start seeing problems at lower impedances. In general, I think that sampling period should be at least 3.5 times the RC time constant of the source resistance and the added capacitance. For single-ended, 16-bit measurements with short sample times, the KL25Z hardware averaging has a period of 25 ADC clock cycles and PteroDAQ is set up to use a 6MHz ADC clock, so the samples are at 240kHz, which would suggest a maximum source impedance of 2.5kΩ for a 470pF capacitor using hardware averaging. This seems consistent with the measured data.

I realized this morning that I did not need to just conjecture the noise on the pin—I could stick an oscilloscope on it and measure it. I used a 47kΩ series resistor (so that the 1MΩ || 10pF load of the Bitscope oscilloscope would not make a huge difference) and a 10 Hz input from the FG085.  I set the PteroDAQ sampling rate to 3750Hz, so that there would be about equal time for the 32 samples and for recovery between them.  I captured single traces and got fairly consistent results.  Here is an example:

This trace at 50mV/division and 20µs/division shows the 240kHz noise from the sample-and-hold circuitry for the first half, and the much smaller noise when not sampling for the second half.  This trace was done without the 470pF capacitor.

This trace at 50mV/division and 20µs/division shows the 240kHz noise from the sample-and-hold circuitry for the first half, and the much smaller noise when not sampling for the second half. This trace was done without the 470pF capacitor.

The noise injected by the sample-and-hold circuitry is about 190mV peak-to-peak with the 47kΩ resistance, or about 4µA.

Adding the 470pF capacitor reduces the peak-to-peak noise to about 16mV or 340nA, but there is enough of a bias to the noise that the error is much larger, as seen by the slow decay back to the correct value in the following trace:

At 10mV/division and 20µs/division, this trace shows both the reduction in noise from using a 470pF  capacitor and slow recovery to the correct voltage at the end of the 32 samples. The time constant for 470pF times 47kΩ is about 22µs, about 5.3 times the sampling rate (or about 20 times longer than desirable for accurate reading).

At 10mV/division and 20µs/division, this trace shows both the reduction in noise from using a 470pF capacitor and slow recovery to the correct voltage at the end of the 32 samples. The time constant for 470pF times 47kΩ is about 22µs, about 5.3 times the sampling rate (or about 20 times longer than desirable for accurate reading).

The injection of noise back into the circuit being tested is a particularly nasty property for test equipment to have. One could avoid it by adding a unity-gain buffer before the pin, which would have three good effects:

  • The input impedance would now be the impedance of the op amp, which can be in the 10GΩ range (for the cheap MCP6004 op amps we use in class)
  • If there was noise from the microprocessor, it would not be injected into the circuit being tested.
  • The source impedance for the analog-to-digital converter would now be around 40Ω (for the MCP6004 op amps), so all these noise problems would go away.

There is one downside to using a unity-gain buffer: you get some non-linearity near the power rails, so the range of useful operation is reduced somewhat.

So when using the PteroDAQ, it is important to pay attention to the source impedance.  When the source impedance, R, is high, one can either

  • add capacitance to reduce the switching noise of the sample-and-hold circuitry (resulting in an RC time constant >3.5 times the sampling period, which can be user-specified (no averaging), 4.1667µs (single-ended channels), or 5.6667µs (differential channels) on the KL25Z; or
  • add a unity-gain buffer to separate the input from the pin.

The noise consideration is a bigger constraint on operation than the input impedance of the analog-to-digital converter pins, which made my attempts to characterize the input impedance somewhat quixotic.

2015 July 11

Improving PteroDAQ

Filed under: Circuits course,Data acquisition — gasstationwithoutpumps @ 19:53
Tags: , , , , ,

For the past couple of years, I’ve been using the PteroDAQ data acquisition system that my son wrote for the KL25Z board (a second-generation system, replacing the earlier Arduino Data Logger he wrote).  He has been working on and off on a multi-platform version of PteroDAQ for over a year, and I finally asked him to hand the project over to me to complete, as I want it much more than he does (he’d prefer to spend his time working on new products for his start-up company, Futuristic Lights).

It has been a while since he worked on the code, and it was inadequately documented, so we’ve been spending some time together just digging into the code to figure out the interfaces, which I’ve been adding comments and docstrings to, so that I can debug the code.  Other than the lack of documentation, the code is fairly well written, so figuring out what is going on is not too bad (except in the GUI built using tkinter—like all GUI code, it is a complicated mess, because the APIs for building GUIs are a complicated mess).

The man goal of his multi-platform code was to support Arduino ATMega-based boards and the KL25Z board, while making it relatively easy to add more boards in future.  The Arduino code is compiled and downloaded by the standard Arduino IDE, while the KL25Z board code is compiled and downloaded with the MBED online compiler.  He has set up the software with appropriate #ifdef checks, so that the same code files can be compiled for either architecture.  The knowledge of what features are available on each board and how to request them is stored in one module of the python program that runs on the host computer.  As part of the cleaning up, we’ve been moving some of the code around to make sure that all the board-specific stuff is in that file, with a fairly clean interface.

He believed that he had gotten the new version working on the Arduino boards, but not on the KL25Z board.  It turned out that was almost true—I got the system working with the Leonardo board (which uses USB communication and has a weird way to get reset) almost immediately, but had to do a number of little bug fixes to get it working with other Arduino boards (which use UART protocol and a different way of resetting). It turned out that the system also worked with the KL25Z after those bug fixes, so he was very close to having it ready to turn over to me.

One of the first things I did was to time how fast the boards would run with the new code—the hope was that the cleaner implementation would have less overhead and so support a higher sampling rate.  Initial results on the Arduino boards were good, but quite disappointing on the KL25Z boards, which use a faster processor and so should be able to support much higher speeds.  We tracked the problem down to the very high per-packet overhead of the USB packets in the mbed-provided USBSerial code.  (He had tried writing his own USB stack for bare-metal ARM, but had never gotten it to work, so we were using the mbed code despite its high overhead.)

There was a simple fix for the speed problem: we stopped sending single-character packets and started using the ability of the MBED code (and the Arduino code for the Leonardo) to send multi-character packets.  With this change, we got much better sampling rates (approximate max rate):

channels Leonardo Uno/Duemilanove/Redboard KL25Z 32x avg KL25Z, 1x avg
0  13kHz  4.5kHz  8.2kHz
1 analog  5kHz  2.7kHz  5.5kHz  8.1kHz
2 analog  3kHz  1.9kHz  3kHz  8.1kHz
7 digital  6.5kHz  3.4kHz  8.2kHz

It is interesting that the Leonardo (a much slower processor) manages to get a higher data rate than the KL25Z when sending just time stamps.  I think that I can get another factor of 3 or 4 speed on the KL25Z by flushing the packets less often, though, so I’ll try that.

By flushing only when needed, I managed to improve the KL25Z performance to

channels KL25Z 32x avg KL25Z, 1x avg
0  17kHz
1 analog  6.3kHz  10kHz ??
2 analog  3.3kHz

Things get a bit hard to measure above 10kHz, because the board runs successfully for several hundred thousand samples, then I start losing characters and getting bad packets. The failure mode using my son’s faster Linux box is different: we lose full packets when going too fast—which is what PteroDAQ is supposed to do—and the speed at which the failure starts happening is much higher (maybe 23kHz). In other words, what I’m seeing now are the limitations of the Python program on my old MacBook Pro. It does bother me that the Mac seems to be quietly dropping characters when the Python program can’t clear the USB serial input fast enough.

The KL25Z slows down when doing the 32x hardware averaging, because the analog-to-digital conversion is slow—particularly when doing 32× hardware averaging.  I think that we’ve currently set things up for a 6MHz  ADC clock, with short sampling times, which means that a single-ended 32× 16-bit conversion takes around 134µs and the sampling rate is limited by the conversion times (differential measurements are slower, around 182µs).

There is a problem in the current version of the code, in that interrupts that take longer to service than the interrupt time result in PteroDAQ lying about its sampling rate.  I can fix this on the KL25Z by using a separate timer, but the Arduino boards have rather limited timer resources, and we may just have to live with it on them.  At least I should add an error flag that indicates when the sampling rate is higher than board can handle.

We had a lot of trouble yesterday with using the bandgap reference to set the voltage levels.  It turns out that on the Arduino boards, the bandgap channel is a very high impedance, and it takes many conversion times before the conversion settles to the final value (nominally 1.1V).  Switching channels and then reading the bandgap is nearly useless—the MUX has to be left on the bandgap for a long time before reading the value means anything.  If you read several bandgap values in quick succession, you can see the values decaying gradually from the value of the previously read channel to the 1.1V reference.

The bandgap on the KL25Z is not such a high-impedance source, but there is some strange behavior when reading it with only 1× averaging—some values seem not to occur and the distribution is far from gaussian.  I recorded several thousand measurements with 1×, 4×, 8×, 16×, and 32× averaging:

The unaveraged reading seems to be somewhat higher than  any of the hardware-averaged ones.

The unaveraged (1×) reading seems to be somewhat higher than any of the hardware-averaged ones.

I was curious about how the noise reduced on further averaging, and what the distribution was for each of the averaging levels. I plotted log histograms (using kernel-density estimates of the probability density function: gaussian_kde from the scipy python package) of the PteroDAQ-measured bandgap voltages.  The PteroDAQ is not really calibrated—the voltage reference is read 64 times with 32× averaging and the average of those 64 values taken to be 1V,  but the data sheet says that the  bandgap could be as much as 3% off (that’s better than the 10% error allowed on the ATMega chips).

Without averaging, there is a curious pattern of missing values, which may be even more visible in the rug plot at the bottom than in the log histogram.

Without averaging, there is a curious pattern of missing values, which may be even more visible in the rug plot at the bottom than in the log histogram.

The smoothed log-histogram doesn't show the clumping of values that is more visible in the rug plot.

The smoothed log-histogram doesn’t show the clumping of values that is more visible in the rug plot.

With eight averages, the distribution begins to look normal, but there is still clumping of values.

With eight averages, the distribution begins to look normal, but there is still clumping of values.

With 16 averages, things look pretty good, but mode is a bit offset from the mean still.

With 16 averages, things look pretty good, but mode is a bit offset from the mean still.

Averaging 32 values seems to have gotten an almost normal distribution.

Averaging 32 values seems to have gotten an almost normal distribution.

Interestingly, though the range of values reduces with each successive averaging, the standard deviation does not drop as much as I would have expected (namely, that averaging 32 values would reduce the standard deviation to about 18% the standard deviation of a single value). Actually, I knew ahead of time that I wouldn’t see that much reduction, since the data sheet shows the effective number of bits only increasing by 0.75 bits from 4× t0 32×, while an 8-fold increase in independent reads would be an increase in effective number of bits of 1.5 bits.  The problem, of course, is that the hardware averaging is of reads one right after another, in which the noise is pretty highly correlated.

I think that the sweet spot for averaging is the 4× level—almost as clean as 32×, but 8 times faster.  More averaging improves the shape of the distribution a little, but doesn’t reduce the standard deviation by very much.  Of course, if one has a low-frequency signal with high-frequency noise, then heavier averaging might be worthwhile, but it would probably be better to sample faster with the 4× hardware averaging, and use a digital filter to remove the higher frequencies.

The weird distribution of values for the single read is not a property of the bandgap reference, but of the converter.  I made a voltage divider with a couple of resistors to get a voltage that was a fixed ratio of the supply voltage (so should give a constant reading), and saw a similar weird distribution of values:

The distribution of single reads is far from a normal  noise distribution, with fat tails on the distribution and clumping of values.

The distribution of single reads is far from a normal noise distribution, with fat tails on the distribution and clumping of values.

 

With 32× sampling, the mean is 1.31556 and the standard deviation 5.038E-04, with an excellent fit to a Gaussian distribution.

2014 January 31

Wire loop vs. twisted pair try 1

Filed under: Circuits course — gasstationwithoutpumps @ 21:54
Tags: , , , ,

I was thinking about things we might do in the first lab day for the Applied Circuits course next quarter, now that I have 2 lab sessions a week.  The first, obvious thing is to unpack the lab kits and identify the different components, labeling the capacitor zip locks with values of the contained capacitors, and learning to use the wire strippers, the power supplies, and the voltmeters.

I’m thinking that I should have the students make a 3′ red-black twisted pair and pack it with their parts, so that they don’t try getting new power wires every week.

One experiment I was considering having them do is to look at the AC signal from a large loop of wire and from twisted pair, to see the difference in noise pickup.  I tried doing this at home today, with rather disappointing results.  The large loop did not pick up more 60Hz noise than the twisted pair when viewed on either my Kikusui oscilloscope or by Bitscope USB oscilloscope.  In fact I couldn’t see any noise with either one.

I tried adding my 1500-gain EKG amplifier as a front end, and then I could see about 60μV noise, but that did not change whether I used a long loop, a twisted pair, or a very short loop between the instrumentation amp inputs.

I first tried looking at how much noise there was from the Bitscope with the DP01 active probe using AC coupling:

Looking at shorted inputs  for the AC-coupled DP01 active probe in its high gain mode.  This is 2mV/division, 100µs/division, "macro" mode.  The noise is a couple of mV.

Looking at shorted inputs for the AC-coupled DP01 active probe in its high gain mode. This is 2mV/division, 100µs/division, “macro” mode. The noise is a couple of mV, about the same as the noise level without the DP01, but the resolution is much better with the DP01.  The high-speed trace here is to show the component of the noise that is around 30.8kHz, which is about as big as the 60Hz component.  The 30.8kHz noise is most likely from the USB power supply into the Bitscope.  The Bitscope does not appear to have as good power-supply noise rejection as one might want.

I then hooked up the EKG amplifier board (which should have a gain around 1508) using a separate power supply, and looked at the noise on the Vref signal (which is just buffered from a voltage divider on the power supply).

At 2mV per division and 10ms per division, we can see a little 60Hz noise added to the high-frequency noise of the Bitscope, but the noise is still only 2–3mV, which would be an input-referenced noise of about 2µV given the amplifier gain around 1500.

At 2mV per division and 10ms per division, we can see a little 60Hz noise added to the high-frequency noise of the Bitscope, but the noise is still only 2–3mV, which would be an input-referenced noise of about 2µV given the amplifier gain around 1500.

The noise on Vref is not much more than noise inherent to the Bitscope and is very similar to the noise I see looking just at the output of the power supply without the EKG attached.
Next I looked at the output of the EKG, with both inputs shorted to Vref.

This is now 40mv/division and 10ms/division looking at the output of the EKG amplifer with both inputs shorted to Vref.  The output noise is around 90mV, so the input-referenced noise is around 60µV.

This is now 40mv/division and 10ms/division looking at the output of the EKG amplifer with both inputs shorted to Vref. The output noise is around 90mV, so the input-referenced noise is around 60µV.

Now we see a signal that is not just Bitscope or power supply noise, and have a noise floor for looking at signals at the input of the EKG amplifier.

Unfortunately, replacing the short with a large loop of wire does not appreciably change the signal, but if I connect the two EKG inputs to Vref via separate 2.2MΩ resistors, I get a large output:

200mV/division, 10msec/division.  Inputs of EKG amplifier separately connected to Vref via 2.2MΩ resistors.  This signal is about 1.04V peak-to-peak (so the input is about 690µV.

200mV/division, 10msec/division. Inputs of EKG amplifier separately connected to Vref via 2.2MΩ resistors. This signal is about 1.04V peak-to-peak (so the input is about 690µV).

The output can be changed substantially by putting my hand near the resistors—the 60Hz noise appears to be capacitively coupled into the amplifier. I can reduce the peak-to-peak voltage to about 500mV  (that is around 300µV at the input) and make it have a larger 120Hz that is almost 10dB larger than the 60Hz component, , just by moving my finger around near the resistors. I can also raise the signal until the EKG amplifier is swinging rail-to-rail (at least 3mV at the input).

So I don’t have a demonstration circuit for electromagnetic pickup here, but I do have one for capacitive coupling.  To detect currents induced in a loop, I probably need a transimpedance amplifier to detect small currents, rather than an instrumentation amplifier to detect small voltages .  I’ll leave that for a separate post.

2013 April 21

Noise in nanopores

Filed under: Uncategorized — gasstationwithoutpumps @ 16:32
Tags: , , , ,

I’ve been trying to understand the sources for noise in current measurements through nanopores, so that I can better understand the signals that are generated in the nanopore lab.  I’ve not studied noise in small signals before, so I’ve been having to rely on Wikipedia for information about sources of noise.  I’ve probably missed some important ones and would appreciate those more knowledgeable pointing out important noise sources that I missed.

I think that there are two main noise sources:  the nanopore itself and the resistors used in converting the nanopore current to a voltage in the first stage of amplification.  Each of these noise sources has two types of white noise: thermal noise (dependent on temperature) and shot noise (independent of temperature).

Thermal noise

The RMS current for thermal noise is i_{\mbox{\scriptsize thermal}} = \sqrt{\frac{4k_B T \Delta f}{R}} for a resistance of R at temperature T, with kB being Boltzmann’s constant, 1.3896593E-23 J/°K, and \Delta f being the bandwidth of the filter looking at the noise.  Normally, nanopore scientists don’t report the resistance of the nanopore, but the bias voltage and the DC current through the pore, but we can use Ohm’s law to rewrite the noise formula as i_{\mbox{\scriptsize thermal}} = \sqrt{\frac{4k_B T I \Delta f}{V}}.  A typical setup for a nanopore may have a voltage of 180mV and 60pA (for an open channel in 0.3M KCl), and a temperature of 25°C = 298.15°K, for 4 k_B T = \mbox{16.573E-21 J}, and noise of \mbox{2.35E-15} \sqrt{\Delta f} A/\sqrt{\mbox{Hz}}.

Shot noise

The RMS current for shot noise is i_{\mbox{\scriptsize shot}} = \sqrt{2 q I \Delta f}, where q is the magnitude of the charge of the carriers (here 1.60217646E-19 C, since the charge carriers are K+ and Cl), and I is the DC current.  Again \Delta f is the bandwidth of the filter looking at the noise.  For a current of 60pA, the shot noise would be \mbox{3.259E-15} \sqrt{\Delta f} A/\sqrt{\mbox{Hz}}, slightly more than the thermal noise.

Combined noise in nanopore

The combined noise in the nanopore from both shot noise and thermal noise should then be i_{\mbox{\scriptsize pore}} = \sqrt{\left(2 q + \frac{4k_B T}{V}\right)I \Delta f}, which for 180mV and 60pA would be \mbox{4.02E-15} \sqrt{\Delta f} A/\sqrt{\mbox{Hz}}.  If a 1kHz low-pass filter is used, that makes an RMS noise level of 0.127pA, and with a 10kHz low-pass filter, 0.4pA.

Increasing the ionic concentration would provide a larger DC current (current proportional to concentration), and the noise current only grows with the square root of the DC current, so the signal to noise ratio also grows with the square root of the ionic concentration.

Amplifier noise

These noise levels are lower than what is actually observed, of course, because we haven’t taken into account noise generated in the first stage of amplification.

In the UCSC nanopore lab, they use an Axon Axopatch 200B Capacitor Feedback Patch Clamp Amplifier, which costs about $2000 used (if you have to ask the new price, you can’t afford it—you can get a quote, but there is no list price).  The web site claims “By introducing active cooling of components in the headstage to well below 0°C, the open-circuit noise in patch mode has been reduced to unprecedented levels, as low as <15 fA (RMS) at 0.1–1 kHz.”  But the lab uses a resistive headstage (so as to be able to make DC current measurements), with a 500MΩ feedback resistor in the current-to-voltage converter, which introduces thermal noise of about \mbox{5.76E-15} \sqrt{\Delta f} A/\sqrt{\mbox{Hz}}, larger than the nanopore noise. The shot noise for the resistor in the I-to-V converter should be the same as as the shot noise for the nanopore, since they have the same DC current. Combining the thermal and shot noises for both the nanopore and the resistor at 180mV and 60pA, I estimate \mbox{7.74E-15} \sqrt{\Delta f} A/\sqrt{\mbox{Hz}}, which would be 0.244pA at 1kHz, 0.547pA at 5kHz, and 0.774pA at 10KHz.  The noise levels observed in the signals are close to those levels, so there is not much that can be done to improve the amplifiers to get a better signal-to-noise ratio.

We could be interested in lower-cost amplifiers than the $2k lab instrument, but a very low noise off-the-shelf op amp (like the AD8432 ) has a noise level of about 2.0pA\sqrt{\Delta f}/\sqrt{\mbox{Hz}}, which is much larger than what is observed in the lab.  Designs have been done at UCSC for much lower noise amplifiers (Gang Wang; Dunbar, W.B., “An integrated, low noise patch-clamp amplifier for biological nanopore applications,” Engineering in Medicine and Biology Society (EMBC), 2010 Annual International Conference of the IEEE , vol., no., pp.2718,2721, Aug. 31 2010-Sept. 4 2010 doi: 10.1109/IEMBS.2010.5626570) claiming “input referred noise of the amplifier is 0.49 pA RMS over a 5 kHz bandwidth”, from simulation (I don’t know whether they have fabricated and tested the amp yet).

UPDATE 2013 Nov 13:  A student showed me a data sheet for an off-the-shelf ($3–$4) op amp that has low enough noise for this purpose: the AD8625 (or AD8626 or AD8627).  They have only a 0.25pA bias current and a current noise density of 0.4 fA\sqrt{\Delta}/\sqrt{\mbox{Hz}}. That’s actually better than the AxoPatch 200B, whose manual reports about 0.1 pA at 10kHz, which is 1fA \sqrt{\Delta}/\sqrt{\mbox{Hz}}.   Even better specs are available for  the AD549L (about $40) with a 60fA bias current and a current noise density of 0.11 fA\sqrt{\Delta}/\sqrt{\mbox{Hz}}.   Of course, once the amplifiers are this good, most of the noise is coming from thermal noise in the feedback resistor (about 6 fA\sqrt{\Delta}/\sqrt{\mbox{Hz}}), unless one uses capacitive feedback (which precludes measuring DC currents).

I don’t know why I missed these op amp before, as they have been around since 2002 or 2003.

%d bloggers like this: