Tuesday, February 15, 2011

GNU Radio TX/RX text via DQPSK

I am attempting to transfer a text file using the USRP an DQPSK modulation. I connected constellation sinks to both the transmit and receive sides.

TX constellation:


RX constellation:

I am using a packet encoder/decoder on both ends, but the packet_decoder is not producing any output.

UPDATE: So on the RX side it appears my gain was too high. After adjusting it I get much better results.

Monday, February 14, 2011

GNU Radio modulation

Looking at gnuradio-examples/python/digital/tx_voice.py....

self.txpath = usrp_transmit_path.usrp_transmit_path(modulator_class, options)

Leads me to usrp_transimit_path.py, which sets some USRP sink options, then passes the work to transmit_path.py.

The options passed to transmit_path.py include:

  • verbose
  • tx_amplitude
  • bitrate
  • samples_per_symbol

...and then the 'modulator class':

self._modulator_class = modulator_class

    # Get mod_kwargs
    mod_kwargs = \
        self._modulator_class.extract_kwargs_from_options(options)
    
    # transmitter
    modulator = self._modulator_class(**mod_kwargs)
    self.packet_transmitter = \
        blks2.mod_pkts(modulator,
                       access_code=None,
                       msgq_limit=4,
                       pad_for_usrp=True)

Then you connect the 'packet_transmitter', amplitude, and transmit path together.

self.connect(self.packet_transmitter, self.amp, self)

    ...

def send_pkt(self, payload='', eof=False):
    """
    Calls the transmitter method to send a packet
    """
    return self.packet_transmitter.send_pkt(payload, eof)

gnuradio-3.3.0/gnuradio-core/src/python/gnuradio/blks2impl/pkt.py: blks2.mod_pkts


class mod_pkts gets passed an instance of a modulator class (gr_block of heir_block2). It then simply connects a packet input block (gr_message_source(sizeof char, msgq_limit):

self._pkt_input = gr.message_source(gr.sizeof_char, msgq_limit)
    self.connect(self._pkt_input, self._modulator, self)



Modulation classes (tx_voice.py):

from gnuradio import gr, gru, modulation_utils
...
mods = modulation_utils.type_1_mods()
...
    for mod in mods.values():
        mod.add_options(expert_grp)
...
# pass it down: tx_voice.py -> usrp_transmit_path.py -> transmit_path.py -> pkt.py
tb = my_top_block(mods[options.modulation], options)

This particular example generated packets from a message queue attached to a voice coder:

from gnuradio.vocoder import gsm_full_rate
...
voice_coder = gsm_full_rate.encode_sp()

The corresponding C++ block, gsm_fr_encode_sp, has a simple work function:

for (int i = 0; i < noutput_items; i++){
    gsm_encode (d_gsm, const_cast(in), out);
    in += GSM_SAMPLES_PER_FRAME;
    out += sizeof (gsm_frame);
  }

This references C code which does the GSM (de/en)coding.



Here are the modulators available in tx_voice.py:

gnuradio.blks2impl.cpm.cpm_mod
gnuradio.blks2impl.d8psk.d8psk_mod
gnuradio.blks2impl.qam8.qam8_mod
gnuradio.blks2impl.dbpsk.dbpsk_mod
gnuradio.blks2impl.dqpsk.dqpsk_mod
gnuradio.blks2impl.gmsk.gmsk_mod

Implemented as separate Python files in gnuradio-core/src/python/gnuradio/blks2impl. The other blks2 folder ties it all together.

Friday, February 11, 2011

hoc: hoc 4 completed

...on Friday after work? What's wrong with me?

Still includes the modulus operator but I need to fix the previous result command (?).

hoc: hoc3 completed

hoc3 is complete, minus the exercises. I nearly bashed my head into the wall trying to track down the following segfault:

yyval.sym = s;

yyval should be yylval!?!? yyval is defined by yacc, and this is a valid statement. Ugh.

[tja@tja-desktop hoc3]$ ./hoc3
1.5^2.3
 2.5410306
exp(2.3*log(1.5))
 2.5410306
sin(PI/2)
 1
atan(1)*DEG
 45
?
 45

Again, '?' will print the last computed value.

TODO:


  1. 8-6: add support for built-ins with multiple parameters.
  2. 8-7: facility to execute commands (done?)
  3. 8-8: revise math.c to use a table instead

Thursday, February 10, 2011

GNU Radio OFDM Python

from gnuradio import blks2

The code for the module 'blks2' actually resides in a directory 'blk2impl'. It is renamed by the SWIG glue.

./gnuradio-3.3.0/gnuradio-core/src/python/gnuradio/blks2impl/ofdm.py

ofdm.py defines an ofdm_mod class which "modulates an OFDM stream." This program in particular uses the C++ blocks gr_ofdm_mapper_bcv, gr_ofdm_insert_preamble, gr_fft_vcc, gr_ofdm_cyclic_prefixer, and gr_multiply_const_cc.

OFDM modulation:

self._pkt_input = gr.ofdm_mapper_bcv(rotated_const, msgq_limit, options.occupied_tones, options.fft_length)
        
self.preambles = gr.ofdm_insert_preamble(self._fft_length, padded_preambles)
self.ifft = gr.fft_vcc(self._fft_length, False, win, True)
self.cp_adder = gr.ofdm_cyclic_prefixer(self._fft_length, symbol_length)
self.scale = gr.multiply_const_cc(1.0 / math.sqrt(self._fft_length))
        
self.connect((self._pkt_input, 0), (self.preambles, 0))
self.connect((self._pkt_input, 1), (self.preambles, 1))
self.connect(self.preambles, self.ifft, self.cp_adder, self.scale, self)

ofdm.py also has the ofdm_demod class:

GNU Radio OFDM C++ blocks

Descriptions from the gnuradio-3.3.0 documentation.

gr_ofdm_demapper_vcb.


take a stream of vectors in from an FFT and demodulate to a stream of bits. Abstract class must be subclassed with specific mapping.

gr_ofdm_frame_aquisition

take a vector of complex constellation points in from an FFT and performs a correlation and equalization.
This block takes the output of an FFT of a received OFDM symbol and finds the start of a frame based on two known symbols. It also looks at the surrounding bins in the FFT output for the correlation in case there is a large frequency shift in the data. This block assumes that the fine frequency shift has already been corrected and that the samples fall in the middle of one FFT bin.
It then uses one of those known symbols to estimate the channel response over all subcarriers and does a simple 1-tap equalization on all subcarriers. This corrects for the phase and amplitude distortion caused by the channel.

gr_ofdm_mapper_bcv


take a stream of bytes in and map to a vector of complex constellation points suitable for IFFT input to be used in an ofdm modulator. Abstract class must be subclassed with specific mapping.

gr_ofdm_frame_sink


Takes an OFDM symbol in, demaps it into bits of 0's and 1's, packs them into packets, and sends to to a message queue sink.

NOTE: The mod input parameter simply chooses a pre-defined demapper/slicer. Eventually, we want to be able to pass in a reference to an object to do the demapping and slicing for a given modulation type.

gr_ofdm_insert_preamble


insert "pre-modulated" preamble symbols before each payload.

input 1: stream of vectors of gr_complex [fft_length]
These are the modulated symbols of the payload.
input 2: stream of char. The LSB indicates whether the corresponding
symbol on input 1 is the first symbol of the payload or not.
It's a 1 if the corresponding symbol is the first symbol,
otherwise 0.
N.B., this implies that there must be at least 1 symbol in the payload.
output 1: stream of vectors of gr_complex [fft_length]
These include the preamble symbols and the payload symbols.
output 2: stream of char. The LSB indicates whether the corresponding
symbol on input 1 is the first symbol of a packet (i.e., the
first symbol of the preamble.) It's a 1 if the corresponding
symbol is the first symbol, otherwise 0.

Parameters:
fft_length length of each symbol in samples.
preamble vector of symbols that represent the pre-modulated preamble.

gr_ofdm_cyclic_prefixer


adds a cyclic prefix vector to an input size long ofdm symbol(vector) and converts vector to a stream output_size long.

gr_ofdm_sampler


"Does the rest of the OFDM stuff"

Wednesday, February 9, 2011

C++ pseudo static map initialization

std::pair map_data[] = {
    std::make_pair(1, "a"),
    std::make_pair(2, "b"),
    std::make_pair(3, "c")
};

std::map my_map(map_data,
    map_data + sizeof map_data / sizeof map_data[0]);

Python itertools

import itertools

Infinitely iterate over an iterator. By default, iterators throw an exception once the last object is retrieved.

my_iterator = itertools.cycle(iterator)

Monday, February 7, 2011

Ch. 2: A Simple Compiler

Syntax-Directed Translation

A syntax-directed definition uses a context-free grammar to specify the syntactic structure of the input.

A syntax-directed definition defines semantic rules.

For example, for infix to postfix translations:

expr -> expr1 + term ||| expr.t := expr1.t || term.t || '+'

Depth-First Traversal

Visits child nodes starting from a root node going from left to right.

procedure visit (n: node);
begin
    for each child m of n, from left to right do
      visit(m);
    evaluate semantic rules at node n
end


Translation Schemes

Like a syntax-directed definition, except that the order of evaluation of the semantic rules is explicit.

From hoc:
expr:    | expr '+' expr    { $$ = $1 + $3; }

Parsing


Determine if a string of tokens can be generated by a given grammar. While compilers may not actually construct a parse tree, they must be able to in order to guarantee correctness.

Top-Down Parsing


Starting from the root, repeat the following:

  1. At a node select a production and construct its children for symbols on the right side of the production.
  2. Find the next node to construct a subtree.
To be continued...


GNU Radio Scheduler

From some GNU Radio documentation:

while enabled and nalive > 0 do
  for i = 1, 2, ...,#blocks do
    if sufficient room in output port bu!ers for block i
      and data at input ports of block i then
        invoke general work() on block i ;
    end if
  end for
end while

  • The single-threaded scheduler <gr_scheduler_sts> is essentially deprecated
  • GNU Radio scheduler operates on flow-graphs composed of processing blocks
  • A thread is created for each block, they are managed together in a thread_group list

Thread Creation

std::auto_ptr thrd(new boost::thread(threadfunc));

From discuss-gnuradio


On Jan 28, 2011, at 2:23 PM, Ben Hilburn wrote:
> As I understand it, there is code in the GNURadio scheduler stuff that
> manages block scheduling, to some degree.
> 
> I'm aware of the kernel's role in switching between the threads
> themselves, but I was under the impression that block scheduling, was
> at least in some part, influenced by GNU Radio.
> 
> If this is incorrect though, someone please correct me!

Yes, this is true, Ben. The OS handles the basics of thread execution, but the
TPB scheduler does handle "when" a block's "general_work" method is actually
called. See gnuradio-core/src/lib/runtime, files "gr_tpb_thread_body.cc"
(which for the most part is a simple loop calling the block executor and
neighbor blocks when things change) and "gr_block_executor.cc" (which is where
the meat of what you're looking for it, I believe). For the latter, you can
set ENABLE_LOGGING to 1, which should give you an idea of what the "decision
process" is.

I think the general idea goes roughly like this: When data in a given buffer
changes (whether through generated or consumed items), each block that is
associated with that buffer checks to see if there is "enough" input data and
output buffer space for a "reasonable sized" computation. If not, then go back
and wait for buffers to change; if so, then do the computation (call
"general_work"). You'll need to look through the code to determine what
"enough" and "reasonable sized" mean -- looking at the debug log might help as
well.

It's been a long time since I've thought about these algorithms, so hopefully
the above is reasonably correct. And, I hope this helps! - MLD

So, gr_block_executer is the one actually doing the work - it calls each block's general_work() function.

Enable Logging: scheduling problems are identified and solved much easier if logging is enabled. In gr_block_executer.cc define ENABLE_LOGGING = 1. This creates log files in the current working directory which include information about the number of items passed to general_work() and the value returned.

Wednesday, February 2, 2011

hoc: hoc1 and hoc2 complete

On a whim I picked up Kernighan and Pike's The Unix Programming Environment and decided to fully implement High-Order Calculator (hoc) - the programming language used as a development case in the book. At least, so far I implemented hoc1 and hoc2, including the exercises (extensions).

The development case, as it is, describes the development of a simple programming language using yacc and lex over six stages. hoc 1 and 2 represent stages 1 and 2.

So, hoc2 has the following capabilities:
  • +,-,*,/,% floating point values
  • nested parenthesis
  • simple variables - a single lower case letter can be assigned a value
  • expression terminating semicolon
    • 2+3; 4*2; a=7.4;
  • a basic system of interpreter commands is included, with only a single command: '?'
    • typing '?' will print the result of the previous computation
Now come the next 4 stages...

Tuesday, February 1, 2011

GNU Radio bug #199

Update:

After much reading and testing, I simply added a call to PyEval_InitThreads() which got rid of the segmentation fault. As a bonus, my detector was working as well.



Well, I am pretty sure this is the same bug.

http://gnuradio.org/redmine/issues/show/199

It seems that once the GNU Radio teamed moved from Python 2.4 to 2.5 the problem went away...for them at least. I have been running the stock (64 bit) Fedora Python2.7 and got this same error. The system seg-faults when it trys to return from Python code back into C++ land. More specifically, on this line from ./gnuradio-3.3.0/gnuradio-core/src/lib/swig/gnuradio_swig_py_general.cc:

~ensure_py_gil_state() { PyGILState_Release(d_gstate); }

So for whatever reason, on my system, this line causes a segmentation fault. Now it is apparently working fine on unmodified GNU Radio examples (like gr_bin_statistics_f). I think it's time to notify the GNU Radio mailing list.

Turn's out this is a some-what common issue among C programs calling Python code. However, it doesn't appear to have a quick solution.


(gdb) c
Continuing.
[New Thread 0x7f4ec2ec5700 (LWP 2747)]
[New Thread 0x7f4ec26c4700 (LWP 2748)]
[New Thread 0x7f4ec1ec3700 (LWP 2749)]
[New Thread 0x7f4ec16c2700 (LWP 2750)]
[New Thread 0x7f4ec0ec1700 (LWP 2751)]
[New Thread 0x7f4eb3fff700 (LWP 2752)]
[New Thread 0x7f4eb37fe700 (LWP 2753)]
[New Thread 0x7f4eb2ffd700 (LWP 2754)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x7f4eb2ffd700 (LWP 2754)]
0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
(gdb) bt
#0  0x00000036b3e0db00 in sem_post () from /lib64/libpthread.so.0
#1  0x00000036c1317679 in PyThread_release_lock () from /usr/lib64/libpython2.7.so.1.0
#2  0x00007f4ec5aa7c1f in ~ensure_py_gil_state (this=0x1112df0, x=0)
    at gnuradio_swig_py_general.cc:5582
#3  gr_py_feval_dd::calleval (this=0x1112df0, x=0) at gnuradio_swig_py_general.cc:5593
#4  0x00007f4ec7112b60 in gr_noise_level_f::tune_window (this=0x10ff680, 
    target_freq=) at gr_noise_level_f.cc:97
#5  0x00007f4ec711353b in gr_noise_level_f::work (this=0x10ff680, noutput_items=7, 
    input_items=, output_items=)
    at gr_noise_level_f.cc:115
#6  0x00007f4ec71ae704 in gr_sync_block::general_work (this=0x10ff680, 
    noutput_items=, ninput_items=, 
    input_items=, output_items=) at gr_sync_block.cc:64
#7  0x00007f4ec7194cd4 in gr_block_executor::run_one_iteration (this=0x7f4eb2ffcd90)
    at gr_block_executor.cc:299
#8  0x00007f4ec71b2322 in gr_tpb_thread_body::gr_tpb_thread_body (this=0x7f4eb2ffcd90, block=...)
    at gr_tpb_thread_body.cc:49
#9  0x00007f4ec71aacd7 in operator() (function_obj_ptr=...) at gr_scheduler_tpb.cc:42
#10 operator() (function_obj_ptr=...)
    at /home/tja/Research/energy/detector/gnuradio-3.3.0/gruel/src/include/gruel/thread_body_wrapper.h:49
#11 boost::detail::function::void_function_obj_invoker0&lt;gruel::thread_body_wrapper\, void>::invoke (function_obj_ptr=...) at /usr/include/boost/function/function_template.hpp:153
---Type  to continue, or q  to quit---
#12 0x00007f4ec6ddf4ef in operator() (this=)
    at /usr/include/boost/function/function_template.hpp:1013
#13 boost::detail::thread_data&lt;boost::function0 >::run (this=)
    at /usr/include/boost/thread/detail/thread.hpp:61
#14 0x00007f4ec6baaa55 in thread_proxy () from /usr/lib64/libboost_thread-mt.so.1.44.0
#15 0x00000036b3e06d5b in start_thread () from /lib64/libpthread.so.0
#16 0x00000036b3ae4a7d in clone () from /lib64/libc.so.6
(gdb) quit