Moving average and history


#1

Hi,

I was about to write a block which calculates the moving average over a
whole vector, when I stumbled upon the way the current moving average
blocks handle the history.

Here’s the code for the float version:

int
gr_moving_average_ff::work (int noutput_items,
gr_vector_const_void_star &input_items,
gr_vector_void_star &output_items)
{
const float *in = (const float *) input_items[0];
float *out = (float *) output_items[0];

float sum = 0;
int num_iter = (noutput_items>d_max_iter) ? d_max_iter :
noutput_items;
for (int i = 0; i < d_length-1 ; i++) {
sum += in[i];
}

for (int i = 0; i < num_iter; i++) {
sum += in[i+d_length-1];
out[i] = sum * d_scale;
sum -= in[i];
}

return num_iter;
}

Here’s what I don’t understand: the state for the MA (as for all
filters) is saved implicitly in the history. However, in this special
case, the state is simply one scalar value (saved in ‘sum’). The way
this is implemented, that state has to be recalculated every time work()
is called. So, why bother with history() and not simply make ‘sum’ a
class property? Would that screw up the scheduling?

The reason I ask is if I write a MA block for vectors, using history()
means keeping vector_length x MA_length samples in some buffer and
running as many extra multiplications every time work() is called. Of
course, I’d prefer to solve this the GNU Radio way, but in this case it
would seem a waste of memory and CPU cycles.

Thanks for any insight,
Martin


#2

On Mon, Feb 09, 2009 at 01:43:41PM +0100, Martin B. wrote:

means keeping vector_length x MA_length samples in some buffer and
running as many extra multiplications every time work() is called. Of
course, I’d prefer to solve this the GNU Radio way, but in this case it
would seem a waste of memory and CPU cycles.

Whoops,
forget about skipping the history(), of course it couldn’t work.
However, by adding a state buffer to the class, the first for() loop
can be skipped. For long MAs, this would save some multiplications and
only increase memory usage by one item_size.

Next time I’ll think about it twice :slight_smile:

Martin


#3

Whoops,
forget about skipping the history(), of course it couldn’t work.
However, by adding a state buffer to the class, the first for() loop
can be skipped. For long MAs, this would save some multiplications and
only increase memory usage by one item_size.

The reason we don’t do that is that floating point numbers will have
numerical errors. If you add thousands of floats together, and then
subtract them all out, you won’t get zero. This error will accumulate
over time and become unstable. That is why we start our accumulation
over again periodically.

Matt