GNU Radio Simulation Tips
Recently, I set up some simulations for my WiFi transceiver. There will be some follow-ups describing the simulation framework and some results, but in this post I wanted to capture things I stumbled upon while setting up the simulations.
Generating Random Messages
The first thing was to generate WiFi frames with a random payload. The WiFi transmitter expects PMTs (i.e., GNU Radio’s Polymorphic Message Type) as input, which can be produced with the Message Source block.
I wanted the payload to be pseudo random and derived from the current repetition to ensure reproducibility.
After some fiddling in Python I came up with
pmt.intern("".join([chr(random.randint(0,255)) for x in (lambda: (random.seed(repetition) == None) and range(pdu_len-24))()]))
It seeds the Python random number generator with the current repetition
and creates a PMT string with pdu_len
random bytes.
(The -24
accounts for WiFi headers like MAC address and sequence number.)
It looks a bit over complicated, but it works.
Let me know if you know of a simpler way to do this.
Note, that this code is executed when the block is initialized. That means that it will always send the same message during a repetition, but produce different messages in different repetitions.
Run to Completion
When doing simulations one usually has a script—or preferable something like a Makefile—that orchestrates the simulations, allows to do runs in parallel, and keeps track of dependencies. For such a process one needs a non-Gui flow graph that shuts itself down properly once it’s finished. In GNU Radio it is often a pain to shutdown a flow graph.
Actually, blocks that recognize that it’s time to shutdown should notify their neighbors and stop their thread. In theory, all threads finish and the application stops. In practice the shutdown process often stops, the flow graph runs for ever, and your simulations never finish.
Under Linux you can use htop to see which threads are still running. Make sure you turn on userland threads, display custom thread names, and display process as tree. Just run your flow graph, wait until it hangs, and check which blocks are still there.
This, for example, tells us that the Wireshark Connector Block and its successor, the File Sink, are still running.
You can also use gdb to check which blocks survived.
$ gdb python
(gdb) run <flow graph>.py
<if it traps in the beginning use 'c' to continue>
<wait until it hangs>
<press CTRL-C>
(gdb) info threads
Id Target Id Frame
41 Thread 0x7fff2b7fe700 (LWP 3196) "python" pthread_cond_wait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_wait.S:185
7 Thread 0x7fffceffd700 (LWP 3160) "file_sink40" pthread_cond_timedwait@@GLIBC_2.3.2 () at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
6 Thread 0x7fffcf7fe700 (LWP 3159) "wireshark_conn2" pthread_cond_timedwait@@GLIBC_2.3.2 ()
at ../nptl/sysdeps/unix/sysv/linux/x86_64/pthread_cond_timedwait.S:238
* 1 Thread 0x7ffff7fc2740 (LWP 3151) "python" 0x00007ffff78e8da3 in select () at ../sysdeps/unix/syscall-template.S:81
Unfortunately, I couldn’t get any method to work on my Mac. I signed gdb as described in this tutorial to get rid of this error message.
Starting program: /Users/basti/usr/homebrew/bin/python sim.py
Unable to find Mach task port for process-id 41997: (os/kern) failure (0x5).
(please check gdb is codesigned - see taskgated(8))
However, I did not manage to display thread names. I also tried lldb, but without success. I’m not sure whether GNU Radio doesn’t set thread names or if they are not displayed. Currently, I can only see how many threads are still active, which might, however, already be a good indicator of where it broke.
Knowing which blocks cause the problem, I thought of a way to shut them down. I added another connection from the Message source, which produces PMT strings, to the Wireshark block. Wireshark actually expects PDUs from the PHY and ignores all strings silently. However, the additional connection makes Wireshark block a direct successor of the message source, which made it shut down properly. Not really nice, but at least it works for now.
Output Filename
For the first set of simulations, I was only interested in the packet delivery ratio and decided to use PCAP packet captures as log files. To avoid that individual runs overwrite each others file, the file name has to depend on the input parameters.
Fortunately, the name field of the file sink is pasted verbatim (indicated by the white background color) in the generated Python script, which allows to set the file name like this.
" + os.path.dirname(os.path.realpath(__file__)) + "/results/sim_%d_%d_%.1f_.pcap" % (repetition, encoding, snr) + "
I found it quite handy to use format strings and explicit formatting of float as it might eases parsing later.
GRC Font Size
I had some issues with font sizes in GTK-based apps like GNU Radio Companion on my Mac. In my case the fonts appeared too small. In order to make some nice screenshots for a paper I tried to figure out what was going on.
Looking at the GRC code, I found a configuration option I was not aware of.
It’s possible to adjust the font size of the block labels on the canvas in your ~/.gnuradio/config.conf
with
[grc]
canvas_font_size = 12
This change resulted in much more readable labels.
Another thing is the text in the menu bar (File, Edit, etc.), which also appeared too small to me. This can be adjusted in your ~/.gtkrc-2.0
with something like
style "user-font" {
font_name = "Lucida Grande 12"
}
widget_class "*" style "user-font"
gtk-font-name="Lucida Grande 12"
With these changes I could tweak GRC to my likings, but that resulted in very large text sizes in applications like Inkscape that are based on XQuartz. Turns out, this was because the DPI of XQuartz was off. For my display I set it to 75 with
defaults write org.macosforge.xquartz.X11 dpi -int 75
Now, everything looks quite right to me.