# Obtain an accurate DC value in an AC+DC signal

Background: My goal is to use an Arduino with two proximity probe sensors to achieve some output.

It is worth mentioning that the output of the proximity probe has two components:

1. DC output that determines the position or the GAP
2. AC component on the top of that DC voltage to determine the vibration

I want to focus on the DC part.

The proximity probe sensor has a negative voltage output from 0 to -20 volt DC, as the Arduino accepts voltage only in the range of 0 to 5 volt, so I decided to use a low-pass filter to remove the AC component and get just the DC component, as this DC component is in the range of 0 to -20 volt. I used an op-amp to invert and to scale down the voltage so instead of being in the range 0 to -20 V, it will be 0 to 5 V.

In this specific question my goal is to get the very accurate DC component. The circuit I implemented is very simple. Basically, it's a low-pass filter with a cut-off frequency of 1 Hz (assuming that my signal won't have a frequency component in the range of 0.00001 - 1 Hz) and then an inverting amplifier with a gain of 1/4 to invert and to scale down the DC voltage.

Here is the circuit:

The output is logical. I used a voltage source with AC and a DC component of -10 V DC offset to simulate the proximity probe signal, and here is the output:

The green line is the DC component which is almost -10 volt, and the blue line is the output inverted and scaled down. Which is almost 2.5.

The problem is with the output. It's almost 2.5 V, and it's oscillating and changing. My application needs an accuracy of millivolt, so this won't be acceptable. How do I get a more stable and accurate output?

Here is the picture of a zoomed-in output:

• Not a 100% sure, but i'm just putting an idea out there. Try simulating it with a filter capacitor at the output. Would this work? – Sada93 May 20 '15 at 8:13
• out of curiosity which software are you using? – Sada93 May 20 '15 at 8:14
• The low pass filter you need to implement would be impractical. Is filtering on the Arduino side optional? – Samee87 May 20 '15 at 8:20
• thanks @Sada93, okay I thought about it, I will try and show you the results. the software is Ltspice – Sabir Moglad May 20 '15 at 8:24
• @user214880 why is it impractical? could you explain more? " Is filtering on the Arduino side optional?" sorry what do you mean, I filter the signal because I need to get the DC component and read it using arduino – Sabir Moglad May 20 '15 at 8:28

Your output looks fine to me.
There is always a period at start up of a filter where its output is off, and it converges to the correct output in a time period related to the cutoff frequency - a lower cutoff for a low pass filter means it will take longer to converge to the true value. This explains the "changing" output of your filter.

Next, is the oscillation. That, too, looks correct. A filter doesn't remove everything out side of its passband, it merely reduces it. Since you are using a simple filter, the reduction isn't very strong. Your output has a much reduced 50Hz wave riding on the DC, which is about what you could expect from the given circuit and simulator settings (signal generator says -10V DC, 1 Volt AC at 50Hz.)

You will need a better filter to reduce the AC. Some other folks here are more capable of giving you tips on that.

You will probably have to live with the period at the beginning where the filter is converging to the proper DC level.

I missed the bit about "millivolt accuracy." Given the Arduino has a 10bit ADC, you will only get 1024 steps for voltages between 0 and 5 volts. That's a tad less than 5 millivolt resolution (4.88mVolt.) So, you aren't gong to get millivolt accuracy just using the ADC.

What you can do is to use a variant of the suggested moving average filter to improve the accuracy at the cost of time resolution. What you would do is use a low pass filter to remove (reduce, really) components above say 1Hz, then average all the ADC values for one second. You would get a value with more bits of accuracy, but only once per second. The ratio between the time period for the average and the real sampling rate tells you how many (theoretical) bits of accuracy you can gain. If you are sampling at 8000Hz, and average every two samples, you can gain one bit of accuracy but only have 4000 samples per second instead of 8000.

That will help with the DC, but not the AC. I don't know what you can do there or if you also need millivolt accuracy there as well.

In response to comments: I see you can do post processing in Matlab. This makes a BIG difference. Use a simple anti-aliasing filter appropriate to your sampling rate and expected signal. Store the sampled data to SD, then process in Matlab.

In Matlab, you can implement the resampling to improve the bit accuracy as mentioned above, as well as using better filters to remove the AC. Since you are working offline, you can use a non-causal filter to remove the AC - this will take care of the convergence problem mentioned above.

Filtering out the DC so that you can process just the AC will also be simpler - non-causal filters will also help here.

• thanks @JRE that makes sense now, waiting for other folks to give some usefull tips – Sabir Moglad May 20 '15 at 8:32
• Filtering can be done in software as well. Perhaps a simple rolling average function might work? – Samee87 May 20 '15 at 8:37
• @user214880 can we get rid of the whole components and use digital filters instead , I have 2 circuits, one to get the DC and another one to get the AC, can that be done digitally ? – Sabir Moglad May 20 '15 at 8:42
• Depending on the sampling rate, it might be a challenge to do all of the filtering with the Arduino. There are libraries for this kind of thing available, though I don't see any notes on performance. You will still need at least one external filter for anti-aliasing filter in front of the ADC. – JRE May 20 '15 at 8:51
• @JRE I agree. Front end filtering in some capacity is needed. I would imagine that 50Hz signal wouldn't be to difficult to account for in software. – Samee87 May 20 '15 at 8:57

If I understand the problem you have to

1. invert the signal
2. Scale the signal
3. Remove the AC component

At the very least an OPAMP is required to forfill #1 and #2 so this active component might as well be used to remove "some" of the AC component.

To remove the AC component however, not just attenuate it. May I suggest a cancellation filter in the digital domain. This uses a DC blocking filter to isolate the ripple which is then removed

$W = W_{ripple} - [W_{ripple} - \frac{1}{N}\sum_{n=0}^{\infty} W_{ripple}]$

The key here is remove, not attenuate.

As a difference equation:

y[0] = x[0] - 0.999*(( x[0]-x[1])+y[1])


This requires 2 storage registers (previous y, previous x). The 0.999 factor essentially governs how well this "filter" rejects AC. the more significant figures the closer the output will be DC BUT will take longer to settle.

Example of an AC signal (6000rpm speed feedback with 1000rpm ripple - forced )

with 0.999

With 0.9999

Remember it is trading off settling time for ripple-free'ness. If the analogue domain can attenuate the AC component enough then you may be able to live with 0.999 coef

This shouldn't cause too much of an overhead on the Arduino while providing a very nice DC output to the rest of your software

• thanks @JonRB that was useful it clarified so many things. the ultimate goal is to get the AC and the DC components not just to remove the AC, I designed a separate circuit for AC oart but I didn't mention. in this case I am moving totally using digital filters. just I have things in mind: what if I started sampling and getting RANDOM data from the sensor before starting the ACTUAL measurement, so this RANDOM samples will compensate for the settling time? subtracting and shifting down the raw signal by DC values will get me the AC or there is a better way to get the AC part? – Sabir Moglad May 21 '15 at 1:23
• hi @JonRB, I have completed my system! when I wanted to try removing this AC in Matlab, I got stuck in the value of y[1], I mean like how to get y[0] before y[1]? – Sabir Moglad Jul 10 '15 at 1:39
• Use a memory block initialised to zero – JonRB Jul 10 '15 at 6:23