Searching \ for '[PIC]: Measuring RPM' in subject line. ()
Help us get a faster server
FAQ page: www.piclist.com/techref/microchip/devices.htm?key=pic
Search entire site for: 'Measuring RPM'.

Exact match. Not showing close matches.
'[PIC]: Measuring RPM'
2001\04\10@132441 by

Thanks Dale,

I don't know a lot of C, but it might be helpful anyway.  The frequency
range will be from about 10Hz to 200Hz.  Fairly low, so it shouldn't be too
hard to get a reading I hope.

Miles.

{Original Message removed}
Sorry, re-posting due to a munged tag yesterday.

In response to a couple of repies I got, here is what I'm using to
calculate RPM from a tach pulse input using the CCP module.

I am using a 20MHz xtal on a 16F8xx processor.  CCP1 is set up to trigger
on the leading edge of the pulse.  CCP1 increments on every instruction
cycle, or 5M times per second.  I get 50 pulses per revolution of the
engine.

([pulses_per_sec]/[numer_of_teeth])  * 60 = RPM.  Pulses per second is
derived from 1/[pulse_length] where the pulse length is in seconds.
Since CCP1 counts at a rate of 5MHz in my case (20MHz clock / 4), I have
to divide the pulse length by 5,000,000 - OR - make things simpler and do
this:

RPM =
((1/([width]/[clock])/[teeth]) * 60 =
((1/(width/5M)/50) * 60 =
(1/width) * 6M =
6M/width

I guess all the above would be obvious to a math whiz, which I'm not, so
it took me a while to figure out just exactly how simple that could be

Now, since I want to smooth out any jitter in the tach pulse signal, I
average out the most recent 20 pulses. I have a CCP1 interrupt function
that populates a 20-element array with the value of the CCP1 capture
register.  Once that array is full, I average the values in the array and
use that number as the pulse width.  You could adjust or eliminate this
based on how many teeth you have on your tach pickup, how fast you want it
to respond to changing RPM, etc.

Now whenever I want to caculate RPM I average the array elements, then
divide that into 6 million and cast the result as a long integer.

while(!tach_good)               // Wait for array to fill
;
for(x=0;x<20;x++) {             // Average pulse widths
period += (float)tach[x];
}
period /= 20;
calctmp = 6000000 / period;     //Calculate engine RPM from timer period
rpm = (long)calctmp;            // Cast this to long int for RPM display

Now bear in mind I have not yet *tested* this code, but I think the theory
is sound and the calculations seem to work.  I think any number of pulses
per revolution and any clock frequency can be accomodated, it will just
change the "magic number" from 6M to something else.  Of course, if anyone
has a better idea I'm more than happy to listen!

Dale
---
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

> Now, since I want to smooth out any jitter in the tach pulse signal, I
> average out the most recent 20 pulses. I have a CCP1 interrupt function
> that populates a 20-element array with the value of the CCP1 capture
> register.  Once that array is full, I average the values in the array and
> use that number as the pulse width.  You could adjust or eliminate this
> based on how many teeth you have on your tach pickup, how fast you want it
> to respond to changing RPM, etc.

I see no reason why this won't work as you intended, but I think there are
better strategies.  Why is a box filter better than a single or double pole
filter?  The latter require much less state and are easy to compute,
especially if the filter fractions are 2**-N.  This can usually be the case
when all you want to do is get rid of some high frequency noise.  If you are
going to use a box filter, why not 2**N samples so that the divide to find
the average turns into a right shift?

Another observation is that you are doing the filtering on the periods.  The
speed value probably has a more predictable dynamic range, making single
pole low pass filtering easier there using fixed point.

In short, I would convert each period to its speed value, then low pass
filter the speed values.  If you don't need a new speed reading as each
tooth goes by, you can set up the timer 1 prescaler to give you only every
Nth pulse.  I doubt you really need 1 part in 6M.

********************************************************************
Olin Lathrop, embedded systems consultant in Littleton Massachusetts
(978) 742-9014, olinembedinc.com, http://www.embedinc.com

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

On Wed, 11 Apr 2001, Olin Lathrop wrote:

> > Now, since I want to smooth out any jitter in the tach pulse signal, I
> > average out the most recent 20 pulses. I have a CCP1 interrupt function
> > that populates a 20-element array with the value of the CCP1 capture
> > register.  Once that array is full, I average the values in the array and
> > use that number as the pulse width.  You could adjust or eliminate this
> > based on how many teeth you have on your tach pickup, how fast you want it
> > to respond to changing RPM, etc.
>
> I see no reason why this won't work as you intended, but I think there are
> better strategies.  Why is a box filter better than a single or double pole
> filter?  The latter require much less state and are easy to compute,
> especially if the filter fractions are 2**-N.  This can usually be the case
> when all you want to do is get rid of some high frequency noise.  If you are
> going to use a box filter, why not 2**N samples so that the divide to find
> the average turns into a right shift?

Good idea,I hadn't thought of that.  Of course in C it's just as simple to
do one as the other in source code, but the assembly code result would be
a lot faster, more elegant and more compact than a floating point divide.

> Another observation is that you are doing the filtering on the periods.  The
> speed value probably has a more predictable dynamic range, making single
> pole low pass filtering easier there using fixed point.
>
> In short, I would convert each period to its speed value, then low pass
> filter the speed values.  If you don't need a new speed reading as each
> tooth goes by, you can set up the timer 1 prescaler to give you only every
> Nth pulse.  I doubt you really need 1 part in 6M.

This sort of goes back to what the intent is.  I don't really expect to
see high frequency noise (but this may be way wrong on my part, of
course).  What I wanted to smooth out was variations in crank rotational
speed (if any) caused by the cylinders firing.  That might cause the
readings displayed on the LCD to be unstable and/or inaccurate.  So I
guess it's intended as a ripple filter and not a HF noise filter, though
on reflection I may need both.

But I see another question.  With my method ("box filter" - interesting
term, hadn't seen that one before), I know that as long as my indicator
flag is good (meaning the array is full) I can get the average of the last
n tach pulses.  I can also "flush" the filter if I need to and know when

If I understand your suggestion correctly, the interrupt routine would
convert the period value to a speed value, then add that to the filter
variable for "n" (maybe 32) samples, then I'd divide that (shift it,
whatever) to get the average. Would that not have to be done to completion
for each reading?  In other words, any time I want to read RPM, I always
have to wait n tach pulses before I can calculate the average, right?

One of my goals was also to keep the interupt routine as short as
possible.  I'm using both the internal hardware USART and a software UART
as well as I2C, and I don't want the CCP interrupt service to cause errors
on the software serial sde.  I have not fully explored that quite yet,
though, and there may be a simple wayto avoid it (like disabling
interrupts while sending with the bit-bang serial routine).

Dale
---
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

> This sort of goes back to what the intent is.  I don't really expect to
> see high frequency noise (but this may be way wrong on my part, of
> course).  What I wanted to smooth out was variations in crank rotational
> speed (if any) caused by the cylinders firing.  That might cause the
> readings displayed on the LCD to be unstable and/or inaccurate.  So I
> guess it's intended as a ripple filter and not a HF noise filter, though
> on reflection I may need both.

What you describe is high frequency noise in this context.  You don't want
the display to jump around.  In otherwords, you don't want to bother the
user with the high frequency content of the rotation speed, just the "trend"
over a few hundred milliseconds.  That IS low pass filtering.  The advantage
of expressing your problem in signal processing terms is that many people
have been there before.  There are many well understood standard techniques
for achieving various results.  Once you admit you have a signal processing
problem you tap into this.

> But I see another question.  With my method ("box filter" - interesting
> term, hadn't seen that one before),

Again, think signal processing.  Think of what the convolution window of a
sliding average looks like - a box.  Yes, this is actually a standard term
for that kind of filter.  A box filter might sound reasonable in time
domain, but its frequency characteristics are far from optimal as a low pass
filter, especially given the amount of computation and state required.

> I know that as long as my indicator
> flag is good (meaning the array is full) I can get the average of the last
> n tach pulses.  I can also "flush" the filter if I need to and know when
> it's ready for use again.

Why would you want to flush the array?  This opens you up to higher aliasing
effects in case there is some periodic high frequency signal (like one
cylinder doesn't fire as well).

> If I understand your suggestion correctly, the interrupt routine would
> convert the period value to a speed value,

No, I never said this had to happen in the interrupt routine.  In fact, I
like doing as little as possible in the interrupt routine.  I've done a
bunch of similar tachometer-type PIC applications.  I only handle the
immediate CCP interrupt in the interrupt.  This includes saving the captured
timer 1 value and adding the time delta into an accumulator, then bumping a
counter to indicate that the accumulator represents one more period.  The
foreground loop does the period to frequency conversion whenever it
discovers at least one period in the accumulator and it gets around to it.
This allows you to handle much higher frequencies with less time spent in
the interrupt routine.  Pulses can come in faster than they can be converted
to frequency.  No matter, since all unconverted pulses are handled at once
whenever the conversion is done.

> then add that to the filter
> variable for "n" (maybe 32) samples, then I'd divide that (shift it,
> whatever) to get the average.

That's very different from what I had in mind.  You are back to a box filter
with this approach.  I suggested a simple single pole low pass filter.  If
FILT is the filtered value to update with a new reading and NEW is the new
reading, then a single pole filter is:

FILT <-- FILT*(1-F) + NEW*F

F is the filter fraction.  It is convenient to have F be 2**-N, like 1/8 or
1/16.  In that case multiplication by F becomes a right shift by N bits.
Multiplication by 1 - F is the original value right shifted N bits
subtracted from itself.  Note that this type of filter is computationally
simple and only requires one state value.  Smaller values of F produce more
filtering.  Note that the filter never changes when F = 0, and there is no
filtering (filter output is always the most recent value) when F = 1.

> Would that not have to be done to completion
> for each reading?  In other words, any time I want to read RPM, I always
> have to wait n tach pulses before I can calculate the average, right?

I would decouple the display update from the frequency calculation from the
pulse capturing.  The pulse capturer maintains the total pulse period and
the number of pulses since the last conversion.  The frequency converter
checks the pulse capture state periodically and does a conversion whenever
it finds at least one pulse has been captured.  It filters the result and
maintains the Official Frequency.  The display update runs 2 to 4 times per
second and writes the current Official Frequency to the display.

********************************************************************
Olin Lathrop, embedded systems consultant in Littleton Massachusetts
(978) 742-9014, olinembedinc.com, http://www.embedinc.com

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

On Wed, 11 Apr 2001, Olin Lathrop wrote:

> > This sort of goes back to what the intent is.  I don't really expect to
> > see high frequency noise (but this may be way wrong on my part, of
> > course).  What I wanted to smooth out was variations in crank rotational
> > speed (if any) caused by the cylinders firing.  That might cause the
> > readings displayed on the LCD to be unstable and/or inaccurate.  So I
> > guess it's intended as a ripple filter and not a HF noise filter, though
> > on reflection I may need both.
>
> What you describe is high frequency noise in this context.  You don't want
> the display to jump around.  In otherwords, you don't want to bother the
> user with the high frequency content of the rotation speed, just the "trend"
> over a few hundred milliseconds.  That IS low pass filtering.  The advantage
> of expressing your problem in signal processing terms is that many people
> have been there before.  There are many well understood standard techniques
> for achieving various results.  Once you admit you have a signal processing
> problem you tap into this.

Guess it never occurred to me, really.  I know squat about filters, as you

{Quote hidden}

Man, here is where I run into a wall.  I am totally unfamiliar with
digital filtering techniques.  What is F?  Is it the inverse of the number
of samples added to the accumulator?  I keep thinking I can see how this
works, but then I plug it into Excel and it doesn't work.  Should've gone
for that engineering degree.  I've tried sample values in a spreadsheet
using several formulas, including

FILT-(FILT*(2^-N))+NEW*(N)
FILT-(FILT*(2^-(1/N)))+NEW*(1/N)

where FILT is the value to be updated, NEW is the accumulated value of the
new reading, and N is the number of new readings that have been
accumulated.  None are making much sense.  I suspect there's just
something I have fundamentally misunderstood about what you're trying
(valiantly) to explain to me.

{Quote hidden}

That's pretty much what I've got now, really.  On every iteration I do
some other data collection and calculation, then average out the most
recent 20 (could be more or less depending on available RAM) pulse widths,
calculate the RPM from that, and display the lot.  Aside from code size
and speed (neither of which is an issue here), what would I gain by using
the single-pole low pass filter?

Dale
---
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov

--
http://www.piclist.com hint: To leave the PICList
piclist-unsubscribe-requestmitvma.mit.edu

Dale,

> >   FILT <-- FILT*(1-F) + NEW*F

> ..... I keep thinking I can  see how this
> works, but then I plug it into Excel and it doesn't work.

the key is the statement

> F is the filter fraction.

A fraction is in the range 0 to 1 so it'll only look good in Excel if you
use values between 0 and 1 (say 0.75)

What you are simply doing is weighting the output result with a proportion
of the OLD output value and a proportion of the NEW input value.

So FILT(N+1) = [ FILT(N) * (1-F)] + [ INPUT(N) * F ]

Where INPUT is obviously your new input sample of the period or whatever.

Example: If N=3 (3rd sample) and F=0.75 then...

FILT(4) = [ FILT(3) * (0.25)] + [ INPUT(3) * 0.75 ]

So it's taking 75% of the new input and 25% of the previous output to
calculate the new output.

If you want the filter to react quickly then F is large but it is also
susceptable to noise and variations in the error of the input and filtering
is small. In the limit where F is 1 the output becomes the input and you get
no filtering.

If the filter reacts slowly then F is small, it is not as susceptable to
noise and variations in the error of the input as you get large filtering.
In the limit where F is 0 the output change slows down so much it actually
never changes.

Somewhere inbetween you'll find a happy medium.

Remember that 8 bit storage values in your PIC can represent fractions
between 0->1, it all depends how you view each bit in the 8bit value. There
have been posts on this before.

For more complex filtering all you are really doing is taking proportions of
older and older output signals along with the new input. So you might store
the last 9 output values, take 1% of the 9th, 5% of the 8th, 8% of the 7th,
15% of the 6th.........78% of the 3rd, 84% of the 2nd and finally say 94% of
the 1st (which is the newest sample). Don't take my numbers as gospel
because they won't work, it's just an illustration. You normally want all
the fractions to add up to 1. But this is for later when you're happy with
this single tap filter.

Pete

--

Dale Botkin <PICLISTMITVMA.MIT.EDU> wrote:

> >   FILT <-- FILT*(1-F) + NEW*F
>
> Man, here is where I run into a wall.  I am totally unfamiliar
> with digital filtering techniques.  What is F?  Is it the inverse
> of the number of samples added to the accumulator?  I keep thinking
> I can see how this works, but then I plug it into Excel and it
> doesn't work. Should've gone for that engineering degree.

Dale:

The people who've been posting on this subject presumably have
engineering degrees, yet they haven't been able to explain the filter
clearly enough for you to understand.  Don't wish for a degree; it'll
only make you part of the problem.

Here's how the filter works:

You want to average a series of values.  As you learned in grade
school, this is accomplished by adding up the values, then dividing
the sum by the number of values in the series.  For example:

Given the 8-element series 3, 5, 7, 1, 8, 4, 2, 2:
3 + 5 + 7 + 1 + 4 + 2 + 2 = 24.
24/8 = 3, so the average (or "mean") is 3.

Easy, right?

In your system, you have a continuous stream of new values coming in,
and you want to average a "window" of the most-recent of them.  In
your example, you used 20 as the size of the window, but for
convenience, I'll use 8.

Let's say that you received the values 3, 5, 7, 1, 8, 4, 2, 2, as
above, but then a new value -- 11 -- comes in, so the full series is:

3 5 7 1 8 4 2 2 11

The sum was 24, and you want to discard the oldest value and add the
newest... So you subtract 3 from 24 and get 21, then you add 11 and
get 32.  You then divide 32 by 8 and get an average of 4.

When the NEXT value came in, you'd subtract 5 from 32, add the new
value, and divide by 8 again... And you'd repeat this forever.

This is all pretty straightforward until you actually try to code it,
at which point you'll find that you have to store as many samples as
you want to average, since you need to know which one to discard when
a new sample comes in.  In this case, only 8 samples need to be
stored, but if you wanted to average 256 samples, you'd need more RAM
than is available in most PICs.

This sucks; we need to store all our received samples in a big array
just so we'll be able to look up the number to subtract from our sum
each time.  If we could just CALCULATE that number without having to
look it up, we wouldn't need the big storage array, right?

Well, we can't calculate it EXACTLY, but if we have a blinding flash
of inspiration, we might figure out that ON AVERAGE, the number we
subtract IS THE MEAN.  In other words, the series:

3 5 7 1 8 4 2 2

has a sum of 24 and a mean of 3, so it's "equivalent" to the series:

3 3 3 3 3 3 3 3

which also has a sum of 24 and a mean of 3.

Therefore... If we know the sum and the mean, we can subtract the
mean from the sum, then add the new sample to get a new sum.  We then
find the new mean by dividing the new sum by the window size (8 in
this example).

The number we subtract will sometimes be smaller than the number we
"should have" discarded, and sometimes it'll be larger, but ON
AVERAGE, it'll be correct... And if you model this algorithm against
the "true" average (with a QuickBASIC program or an Excel spreadsheet
or whatever, you'll see that it tracks very closely.

Ok... This new algorithm can be expressed as:

Old Sum - Old Mean + New Sample
New Mean = -------------------------------
Window Size

The old sum is equal, of course, to the old mean times the window
size, so we can rewrite it as:

(Old Mean * Window Size) - Old Mean + New Sample
New Mean = ------------------------------------------------
Window Size

Simplifying:

Old Mean * (Window Size - 1) + New Sample
New Mean = -----------------------------------------
Window Size

So for my example, it's:

Old Mean * 7 + New Sample
New Mean = -------------------------
8

At this point, it'd probably be instructive to notice that this is
equivalent to:

New Mean = Old Mean * 7/8 + New Sample * 1/8

and if we call the mean "FILT" and assign a constant F to the value
1/8, it becomes:

FILT <-- FILT * (1-F) + NEW*F

which is the equation which you (previously) didn't understand.

Anyway, the point of all this is that if you pick a window size
that's a power of 2, the division by window size and the
multiplication by (window size - 1) are trivially easy, AND... It
doesn't matter how big your window size is; you can have an
arbitrarily-large window size without suffering more than a tiny
penalty in RAM usage and code complexity.

In fact, if you choose a window size of 256, the division completely
DISAPPEARS; it doesn't take ANY code to perform.  To see the code
required to perform this filtering algorithm with a window size of
256 (as I recall, it required less than 20 words of non-looped code),
search the PICLIST archives; I posted it a few months ago.

-Andy

=== Andrew Warren - fastfwdix.netcom.com
=== Fast Forward Engineering - San Diego, California
=== http://www.geocities.com/SiliconValley/2499

--

> >   FILT <-- FILT*(1-F) + NEW*F
>
> Man, here is where I run into a wall.

First of all by "<--" I meant assignment.  (It was supposed to look like a
left arrow.  I don't like using "=" for assignment because "=" is already
used in mathematics as a statement of equality).  In other words, FILT
receives the value of the expression to the right of the "<--".

Suppose I wrote the operation (its not an equation) like this:

FILT <-- FILT*A + NEW*B

Perhaps now it is more obvious that this "blends" FILT and NEW to make the
new value of FILT.  For example, if you simply wanted the average of FILT
and NEW, then A = 1/2 and B = 1/2.  What would you do if you wanted to
"average" the two but wanted one to have a higher weight than the other.
You could, for example use A = 1/4 and B = 3/4.  That would blend the two
but NEW would be weighted higher.  Note that you achieve a weighted blending
between the two input values as long as A + B = 1.  Since A + B = 1, you can
also say A = 1 - B.  Once you have determined B, you no longer have a choice
about A (and vice versa).  So let's re-write the operation to eliminate A
and derive it from B instead:

FILT <-- FILT*(1-B) + NEW*B

Same thing as at top, but I happened to call the blend adjustment factor F

As I said before, this operation is easy to compute when F = 2**-N.  For
example, let's use N = 3.  That means F = 1/8 and 1-F = 7/8.  The filter
operation becomes:

FILT <-- FILT*(7/8) + NEW*(1/8)

This means the latest reading will be responsible for 1/8 the filtered
value, and all other previous readings determine 7/8 of the filtered value.
Also consider the weighting of any one reading.  It starts out at 1/8, after
the next reading is filtered in, it has a weight of 1/8 * 7/8.  After
another reading it is 1/8 * 7/8 * 7/8, then 1/8 * 7/8 * 7/8 * 7/8.  After J
will be 1/8 * (7/8)**J.  Or symbolically F * (1-F)**J.  So now it is obvious
(I hope) that every new reading starts out with a weight of F, then
exponentially decays as new readings are accumulated i| <Fthe filter.

If F = 2**-N, then multiplication by F is just a right shift by N bits.  We
can now re-write the operation in the form most suitable for coding:

FILT <-- FILT - rshift(FILT, N) + rshift(NEW, N)

> where FILT is the value to be updated, NEW is the accumulated value of the
> new reading, and N is the number of new readings that have been
> accumulated.

Forget this "number of accumulated readings".  You don't need to keep any
persistant state except FILT.  You are still thinking "average".  You need
to get past that.

> That's pretty much what I've got now, really.  On every iteration I do
> some other data collection and calculation, then average out the most
> recent 20 (could be more or less depending on available RAM) pulse widths,
> calculate the RPM from that, and display the lot.

Actually that sounds quite differnt from what I suggested, but I don't want
to go there again.

> Aside from code size
> and speed (neither of which is an issue here), what would I gain by using
> the single-pole low pass filter?

The advantages are code size, speed, greatly reduced RAM requirements, and a
somewhat better response characteristic.  It is also benificial to learn
this because someday code size and speed will matter, and it will be useful
to understand how to do a single pole filter.

By the way, a multi-pole low pass filter is just multiple single-pole low
pass filters cascaded.  And a high pass filter is just the original signal
minus the low pass of that signal.  You can do a lot of useful things with
the basic single pole low pass filter building block.

********************************************************************
Olin Lathrop, embedded systems consultant in Littleton Massachusetts
(978) 742-9014, olinembedinc.com, http://www.embedinc.com

--

> Dale:
>
> The people who've been posting on this subject presumably have
> engineering degrees, yet they haven't been able to explain the filter
> clearly enough for you to understand.  Don't wish for a degree; it'll
> only make you part of the problem.
>
> Here's how the filter works:
>
> You want to average a series of values.  As you learned in grade
> school, this is accomplished by adding up the values, then dividing
> the sum by the number of values in the series.  For example:
>
>     Given the 8-element series 3, 5, 7, 1, 8, 4, 2, 2:
>     3 + 5 + 7 + 1 + 4 + 2 + 2 = 24.
>     24/8 = 3, so the average (or "mean") is 3.

Andy, I liked your explanation of how this filter works.  I'd never thought
of it in quite those terms before.

However, "mean" is ***NOT*** "average".  I noticed that whenever you said
"mean" in your description, you really meant "average".  You may understand
the distinction, but this can be a source of confusion for many people.
Sorry to make such a big deal of it, but I don't want high school students
listening in to learn it the wrong way or get confused.

********************************************************************
Olin Lathrop, embedded systems consultant in Littleton Massachusetts
(978) 742-9014, olinembedinc.com, http://www.embedinc.com

--

Olin Lathrop <PICLISTMITVMA.MIT.EDU> wrote:

> Andy, I liked your explanation of how this filter works.  I'd
> never thought of it in quite those terms before.

Thanks, Olin.

> However, "mean" is ***NOT*** "average".  I noticed that whenever
> you said "mean" in your description, you really meant "average".
> You may understand the distinction, but this can be a source of
> confusion for many people. Sorry to make such a big deal of it, but
> I don't want high school students listening in to learn it the
> wrong way or get confused.

Um, no.  I used the word correctly; the mean IS the arithmetic
average.

Perhaps you were thinking of the distinction between the
arithmetic average and the "median"?

-Andy

=== Andrew Warren - fastfwdix.netcom.com
=== Fast Forward Engineering - San Diego, California
=== http://www.geocities.com/SiliconValley/2499

--

On Thu, 12 Apr 2001, Andrew Warren wrote:

> Dale Botkin <PICLISTMITVMA.MIT.EDU> wrote:
>
> > >   FILT <-- FILT*(1-F) + NEW*F
> >
> > Man, here is where I run into a wall.  I am totally unfamiliar
> > with digital filtering techniques.  What is F?  Is it the inverse
> > of the number of samples added to the accumulator?  I keep thinking
> > I can see how this works, but then I plug it into Excel and it
> > doesn't work. Should've gone for that engineering degree.
>
> Dale:
>
> The people who've been posting on this subject presumably have
> engineering degrees, yet they haven't been able to explain the filter
> clearly enough for you to understand.  Don't wish for a degree; it'll
> only make you part of the problem.
>
> Here's how the filter works:

<snip>

Bravo!

> In fact, if you choose a window size of 256, the division completely
> DISAPPEARS; it doesn't take ANY code to perform.  To see the code
> required to perform this filtering algorithm with a window size of
> 256 (as I recall, it required less than 20 words of non-looped code),
> search the PICLIST archives; I posted it a few months ago.

If you want another implementation then check out:

http://www.dattalo.com/technical/software/pic/twist.asm

Scott

--

On Thu, 12 Apr 2001, Peter Betts wrote:

> Dale,
>
> > >   FILT <-- FILT*(1-F) + NEW*F
>
> > ..... I keep thinking I can  see how this
> > works, but then I plug it into Excel and it doesn't work.
>
> the key is the statement
>
> > F is the filter fraction.
>
> A fraction is in the range 0 to 1 so it'll only look good in Excel if you
> use values between 0 and 1 (say 0.75)

I got all that, but didn't quite get where that number was coming from.

{Quote hidden}

NOW I get it.  *I* pick F, arbitrarily, depending on how I want the filter
to act.  Thanks.  That was the piece I was missing.  Now it makes sense,
and I can see where I was getting of track.

Dale
---
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov

--

On Thu, 12 Apr 2001, Andrew Warren wrote:

> Dale Botkin <PICLISTMITVMA.MIT.EDU> wrote:
>
> > >   FILT <-- FILT*(1-F) + NEW*F
> >
> > Man, here is where I run into a wall.  I am totally unfamiliar
> > with digital filtering techniques.  What is F?  Is it the inverse
> > of the number of samples added to the accumulator?  I keep thinking
> > I can see how this works, but then I plug it into Excel and it
> > doesn't work. Should've gone for that engineering degree.
>
> Dale:
>
> The people who've been posting on this subject presumably have
> engineering degrees, yet they haven't been able to explain the filter
> clearly enough for you to understand.  Don't wish for a degree; it'll
> only make you part of the problem.

I have a great deal of respect for most of those who have invested the
time, money and effort into earning a degree.  I also know that there's a
top 10% and a bottom 10% in every class, all of whom get the same diploma.
Around the PICList I think it's heavily weighted toward the top half at
least, and I appreciate the opportunity to learn the theory behind some
of the methods I've learned the hard way.

8< [massive snippage of excellet explanation]...

{Quote hidden}

But I sure do now!!  Of course it's absurdly simpe now, to paraphrase Mr.
Holmes.  Thanks for that.

{Quote hidden}

That applies only to 8-bit sums, though, correct?  I'll have a 16-bit sum,
whether dealing with pulse length or RPM.  But that's moot; now that I
understand the method (thanks to Olin's suggestion and your explanation),
I bet I can save a lot of code space for some other feature I'm sure will
seem like a good idea <grin>.  But of course I probably will appreciate
this exercise even more the next time I need this.

Thanks, guys, you're aces.  It does my aging brain good to hurt once in a
while.

Dale
---
The most exciting phrase to hear in science, the one that heralds new
discoveries, is not "Eureka!" (I found it!) but "That's funny ..."
-- Isaac Asimov

--

Dale Botkin <PICLISTMITVMA.MIT.EDU> wrote:

> > In fact, if you choose a window size of 256, the division completely
> > DISAPPEARS; it doesn't take ANY code to perform.  To see the code
> > required to perform this filtering algorithm with a window size of
> > 256 (as I recall, it required less than 20 words of non-looped
> > code), search the PICLIST archives; I posted it a few months ago.
>
> That applies only to 8-bit sums, though, correct?

Dale:

An 8-bit sum divided by 256 would be sorta pointless, since the
quotient would always be 0; the "20-line" routine I wrote calculates
a full 16-bit sum.

I just found the routine that I posted and discovered that it's only
8 lines of code, not 20:

; 256-WIDE MOVING-WINDOW AVERAGE.  8 WORDS, 8 CYCLES.
;
; ADD A NEW SAMPLE TO OUR RUNNING SUM, SUBTRACT THE OLD AVERAGE,
; THEN DIVIDE BY 256 TO COMPUTE THE NEW MOVING-WINDOW AVERAGE.
;
; ENTER WITH THE NEW SAMPLE IN W.  EXITS WITH THE NEW AVERAGE
; IN W.

ADDWF   SUMLO       ;SUM = SUM + SAMPLE.
MOVF    SUMHI,W     ;(AND WHILE WE'RE HERE, PUT THE OLD
SKPNC               ;AVERAGE IN W).
INCF    SUMHI       ;

SUBWF   SUMLO       ;ADJUST THE SUM BY SUBTRACTING THE OLD
SKPC                ;AVERAGE FROM THE NEW SUM.
DECF    SUMHI       ;

MOVF    SUMHI,W     ;W = THE NEW AVERAGE (ADJUSTED SUM/256)

If you use the above code, you should first initialize SUMHI:SUMLO to
something reasonable... I'd probably just take one sample from my
input, set SUMHI to that value, and clear SUMLO to 0.

-Andy

=== Andrew Warren - fastfwdix.netcom.com
=== Fast Forward Engineering - San Diego, California
=== http://www.geocities.com/SiliconValley/2499

--
http://www.piclist.com hint: The list server can filter out subtopics
(like ads or off topics) for you. See http://www.piclist.com/#topics

More... (looser matching)
- Last day of these posts
- In 2001 , 2002 only
- Today
- New search...