Problem with FIR filter

Hello,

I’ve got a problem with the FIR filters in GNU radio. Basically, at the
moment I’m using it as a platform to run some simulations on (as I’ve
already written a load of code on it for implementing a digital radio),
and
one of the tests I’m doing is trying to find the BER through an
equaliser.
To do this I’m loading a vector source up, running my flow graph with
run()
and upon completion I’m checking a vector sink for errors. This works
fine
for a fixed channel. The channel model I’m now using (a Watterson model
which I’ve coded into the flow graph), however, uses a FIR with a large
number of taps. Now the problem is that every time I run my flow graph
with
run(), which presumably also calls wait(), the FIR seems to start from a
reset state. Is there any way to keep the contents of the FIR’s shift
register in situ through calling run() multiple times? Every other
signal
processing block I have maintains its state through this. For the record
I’m
not calling stop() at all in the code, which I would expect to reset
everything.

I’ve set up a quick test case to describe this problem,

So if I set up a little top_block like this, which just sends a string
of
complex symbols valued +1+0j through a FIR filter, and receives the
result
in block_out;



class my_top_block(gr.top_block):
def init(self):
gr.top_block.init(self)

self.data_rate = 1000;
self.totbits = 0;
self.toterrors = 0;

grey_const = ([1, -1])

length = 2

blank = [0x0,] * (length)
taps = [1,] * 10

self.transmit_source = gr.vector_source_b(blank, False, length);

self.unpack_for_mod = gr.packed_to_unpacked_bb(1, gr.GR_MSB_FIRST)

self.symbol_mapper = gr.chunks_to_symbols_bc(grey_const)

self.fir = gr.fir_filter_ccc(1, taps)

self.v2s = gr.vector_to_stream(1, length)

self.connect(self.transmit_source, self.v2s, self.unpack_for_mod,

self.symbol_mapper, self.fir)

self.connect(self.fir, block_out)

Then if I run it from main() like this;


def main ():

    tb = my_top_block();

    for a in range(20):

        tb.run()

        for n in block_out.data():

        print n

    block_out.clear()

    tb.transmit_source.rewind()

I get as output;


gr_fir_ccc: using SSE
(1+0j)
(2+0j)
(3+0j)
(4+0j)
(5+0j)
(6+0j)
(7+0j)
(8+0j)
(9+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(1+0j)
(2+0j)
(3+0j)
(4+0j)
(5+0j)
(6+0j)
(7+0j)
(8+0j)
(9+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(10+0j)
(1+0j)
(2+0j)
(3+0j)
(4+0j)
(5+0j)
(6+0j)
(7+0j)
(8+0j)
.
.
etc


Which is wrong (after the first 10 outputs the output should always be
(10+0j). Is this a bug? Also does anyone know of any work around?

Cheers,

John

So does no one have any idea about this? It doesn’t seem like it should
be
too difficult to solve, I’ve worked around the problem for the
simulations
I’m currently running but it’s a shame as it reveals a fundamental
incapacity for GNU Radio to perform some other interesting simulations
and
techniques, notably (for me at least) in the area of repeat requests.
Currently I’m implementing a type II HARQ repeat request system by
dynamically reconfiguring a flow graph during operation, this makes use
of
the run() command a lot but if every time I run this command every
filter in
the system is reset it’s going to make it almost impossible to
implement.

Up to now I’ve been really impressed with GNU Radio, even to the point
of
writing a conference paper (for the upcoming NORDIC '10 HF conference in
Sweden) about how good the system is in the lab. I think the API is very
well written and like how the Python scripting aspect allows code
written
for testing and simulation of techniques to be plonked straight into a
working radio. Unfortunately the problem below seems pretty fundamental
and
can be treated (at least in the context of how I’m using the software)
as a
pretty major bug. Does anyone have any opinion on this?

Also if anyone has any information on how the filters are implemented
I’ll
have a go at fixing this myself, I’ve taken a quick look at the filters
folder in the source directory but nothing’s jumping out at me and I’m
currently pretty busy desperately trying to finish off my thesis!

Cheers,

John

On Thu, Jul 22, 2010 at 05:13:09PM +0100, John W. wrote:

So does no one have any idea about this? It doesn’t seem like it should be
too difficult to solve, I’ve worked around the problem for the simulations
I’m currently running but it’s a shame as it reveals a fundamental
incapacity for GNU Radio to perform some other interesting simulations and
techniques, notably (for me at least) in the area of repeat requests.
Currently I’m implementing a type II HARQ repeat request system by
dynamically reconfiguring a flow graph during operation, this makes use of
the run() command a lot but if every time I run this command every filter in
the system is reset it’s going to make it almost impossible to implement.

Part of the challenge in implementing dynamic reconfiguration is what
to do with any intermediate samples left over in the buffers. This
brings up questions such as do the blocks own the buffers? If so,
does the upstream block own the buffer, or the downstream block? What
about the cases where there are multiple downstream readers of a
buffer? What happens if you replace the upstream block? What about
the downstream block?

With regards to the FIRs, I think what you’re seeing is a side effect
of our decision to implement the delay line implicitly. That is, it’s
handled using the “history” mechanism, and involves pre-stuffing the
input buffer with zeros. The chief advantage of this strategy is
performance. This idea combined with the our zero copy MMU circular
buffer trick, means there are no corner cases in the implementation of
the FIRs (no “end-of-buffer wrap around” etc.) and no cycles are
expended storing anything in any delay line. (Having versions of the
FIRs that explicity handle a delay line is definitely possible.)

I understand that you’re relating with this like it’s a serious bug,
but I think it would be more productive if you would specify exactly
the behavior that you want, see if that spec is consistent with
expectations that other folks may have (for their own good reasons),
and then we could discuss where in the code the modifications would
go.

A related question is when (if ever) should runtime state (both
external to a block, like the buffers) and internal state (say PLL
state inside of a block) be reset.

I can see use cases arguing for a number of models. It basically
depends on what you are trying to do.

So, how would you want it to behave?

Some of the primitive operations to consider are tb.start(),
tb.stop(), tb.wait() and tb.run() == { tb.start(); tb.wait(); }

Adding a “reset” method to gr_basic_block is also possible. Of course
then you need to figure out when the runtime (or user) should call it.

When using lock/unlock for dynamic reconfiguration, the internal
represenation of the graph is actually reconfigured in the midst of
the final unlock, and that code uses start() and stop() to pause the
action in the graph while it shuffles everything around.

Again, I think your question/complaint is valid, and figuring out the
desired behavior at all the corner cases is important. Then of course
there’s figuring out how to implement it :slight_smile:

If you get a chance, please have a go at it.

Eric

On Thu, Jul 22, 2010 at 6:48 PM, Eric B. [email protected] wrote:

dynamically reconfiguring a flow graph during operation, this makes use
buffer? What happens if you replace the upstream block? What about
FIRs that explicity handle a delay line is definitely possible.)

I understand, so I’m guessing the operation of the FIRs is a lot more
‘fundamental’ than the other common blocks in that their operation is
quite
intimately tied up with the lower level buffer management of the flow
graph
itself. I’ve read about the history mechanism, and I understand that
when
flow graph edges are reconfigured then not calling it could cause
problems.
However in my case I don’t need the graph edges connecting to the FIR to
change at all, in fact I need them to stay in the exact state that they
were
left in when wait() was called to maintain the data integrity through
these
buffers (and therefore the FIR). To put this into context the part of
the
graph that’s causing a problem is part of the channel model, a Watterson
model which goes something like this;

Signal In

I

v
WGN → Gaussian Filter → Interpolation → Anti-Aliasing filter →
Multiply

I

v

AWGN

                 I

                 v

            Signal Out

The anti-aliasing filter has a large number of taps compared to each
packet
length. I was hoping to send one packet at a time, check it and re-run
the
graph, starting the model from here it left off, the model takes a few
thousand bits sent through it to fill up the buffers, so the first few
packets send through it are received as rubbish. When sending one packet
at
a time between resets, it’s always rubbish! I fixed this by just sending
all
my packets in one transmission and checking them at the block level with
a
custom block, but the repeat request techniques I have require that the
run() method be called.

Looking back at my ARQ test code (which I wrote a while back) I can see
that
it was a bit of a hack, I’m instructing my transmit source to ‘send
nothing’
when a repeat is needed, by this I mean the start point is actually
requested to run the work function again, copy nothing to the buffer and
return 0. The repeat packet generator then generates the actual data to
be
transmitted. This is to fool GNU Radio into believing that its sending
something from the start point of the graph and is clearly related to
this
issue. So me calling this a ‘serious bug’ might have been a little
melodramatic!

I understand that you’re relating with this like it’s a serious bug,

This is a good question and I think it would be nice to build in a
little
more user involvement into this. I think there needs to be a further
state
that can be entered on request, where the graph can be paused under
certain
conditions without causing a reset of some or all of the buffers on the
graph edges, for instance when the graph detects that no further data is
being ‘pulled’ towards the end point (I’m guessing that’s what causes
the
wait() function to return), could the graph, or certain pre-defined
edges of
the graph be put into a ‘paused’ state which maintains residual data
within
the buffers of those edges? This could maybe be defined by the user by
calling a modified connect() function, for example connect_persistent().

When using lock/unlock for dynamic reconfiguration, the internal

represenation of the graph is actually reconfigured in the midst of
the final unlock, and that code uses start() and stop() to pause the
action in the graph while it shuffles everything around.

I looked at the lock() and unlock() functions but I didn’t find them
useful, presumably because of the stop() function called which resets
everything and seems to cause every block to reinitialise.

Again, I think your question/complaint is valid, and figuring out the
desired behavior at all the corner cases is important. Then of course
there’s figuring out how to implement it :slight_smile:

If you get a chance, please have a go at it.

Eric

Where would I start to look at these things? In particular for two usage
cases, one being a modification to the generic FIR functions, whereby
the
filter could be instructed to copy the input buffer into an internal
buffer
when finishing a graph, and then copy this back in lieu of using the
add_history() function; the second being an additional
connect_persistent()
function as described above.

As a side note I have a load of code relating to LDPC, ARQ and turbo
equalisation that I’d like to share, I thought I put a message on this
list
about this a while ago and assumed I was being ignored, but (as you
know:) I
was being a fool and sending it via the wrong email address!

Cheers,

John

Hi all,

Â

I got some periodic phase error in the
output even with external atomic clock connected with USRP2 mother
board. I
tried with GPS signals and signals from the Agilent function generator.
The
error can be more than 15 degree. Does anyone know what may be the
reason?

Thanks