Table of Contents
Using a custom bitstream with a PicoSDR
This tutorial shows how to use a custom bitstream when you are making experiments with PicoSDR. For this we will be using a Nutaq example about OFDM transmission.
This example provides a bitstream and a GNU Radio Companion (GRC) file interacting with the bitstream. All the OFDM work is done within the FPGA. Basically you send raw data to the PicoSDR, the FPGA makes the OFDM modulation, TX antenna of card 2 makes the emission and RX antenna of card 2 receives the signal, the FPGA makes the OFDM demodulation, and data is sent back to the computer. As you can see all the work is done with only 1 PicoSDR but using the antennas, so this is really for demonstration purpose only. (Pro tip : when modulation and demodulation are made by the same FPGA you don't have to take care of the synchronization because both share the same clock).
The GRC file can be found at
/Path/to/toolchain/opt/Nutaq/ADP6/ADP_MicroTCA/sdk/gnuradio/gr-nutaq/examples/ofdm.grc and the bitstream at
/Path/to/toolchain/opt/Nutaq/ADP6/ADP_MicroTCA/sdk/fpga/bin/OFDM_6_6_0_sx315.bit. Copy both to a new folder named
fpga-pico. For example, on
airlock, the toolchain is located in
Edit the .grc file
Let's take a look at
$ gnuradio-companion <path-to-ofdm.grc>.
First, what it is intended to do: we send a video file to RTDEX Sink, and from RTDEX Source we get the constellation points of the OFDM demodulation and the video file is sent to a UDP Sink (in order to open it with VLC for example). We have also a lot of parameter blocks we don't care about in this tutorials (all the custom register blocks are specific to the bitstream).
This example can be used straight away when you are working on your setup, but not in CorteXlab because of the video file over UDP thing. Instead of this, we are going to do something a lot easier, which is sending a constant integer to the PicoSDR, and see if we get it back.
First of all, we need to change the generate option of the graph to 'No GUI'. Thus we can remove the
QT GUI Constellation Sink block and all the other blocks leading to it. You can also remove the
UDP Sink blocks, we don't need them anymore.
Let's get back to the idea of sending a constant number to the PicoSDR and see if we get it back. First thing to do is to add a
Constant Source block (with Int as Output Type, and whatever-you-want as the constant) (block can be found in category
Waveform Generator), and connect it to
RTDEX Sink. Now, how can we check that we receive the right number ? We could send it to a file and then check the file, but this ain't funny. Instead of this, we're gonna use a super cool new type of block added to GNURadio lately (As I am writing this tutorial). I'm talking about
Python Block. With this block, you can create a custom block, with its associated python code easily, without going through an OOT module.
Add a Python Block (from category
Misc), open it, and click on 'Open in Editor'. You can now edit the python code behind the block, and as soon as you save it, changes are repercuted to the block in GRC. You can either write your own code comparing two inputs or use mine :
""" Embedded Python Blocks: Each time this file is saved, GRC will instantiate the first class it finds to get the ports and parameters of your block. The arguments to __init__ will be the parameters. All of them are required to have default values! """ import numpy as np from gnuradio import gr import sys class blk(gr.sync_block): def __init__(self, num_samples=100000): # only default arguments here gr.sync_block.__init__( self, name='Error rate', in_sig=[np.int32, np.int32], out_sig=None ) self._num_samples = num_samples self._idx_sample = 0 self._valid = 0 self._bad = 0 def work(self, input_items, output_items): in0 = input_items in1 = input_items for i in range(len(in0)): if (in0[i] == in1[i]): self._valid += 1 else: self._bad += 1 self._idx_sample += 1 if (self._idx_sample >= self._num_samples): rate = self._valid*100/float(self._num_samples) print "----------------------------------" print "%.3f percent valid over %s samples " % (rate, self._num_samples) print "----------------------------------" return -1 return len(input_items)
The code is quite simple : you take two input streams of int, you compare them over a number of samples defined by the paramter num_samples, and when it's done you print the result of matching sample in percentage and you stop the block.
Save the file in editor and go back to the graph. You now have your new block named 'Error Rate' with two inputs. Connect the constant source and the RTDEX Source blocks to it.
We are now done with the .grc file, don't forget to generate the flow graph. Your graph should look like this :
Create the scenario
In order to run the experiment in CorteXlab we need a description called scenario. Create a file named
scenario.yaml and copy the following in it :
# Example scenario description file # # All lines starting with "#" and empy lines are ignored # Scenario textual description # simple string (a one liner) description: base scenario for CorteXlab # Experiment maximum duration # Time after which the experiment is forced to stop # integer (seconds) duration: 300 # Node list # # format: # # nodes: # (machine): # command: (entry point script relative to the task root) nodes: node31: command: ./ofdm.py bitstreamA: OFDM_6_6_0_sx315.bit
If you translate this file it says “The experiment will run for 5 minutes. The script ofdm.py will be executed on node 31 using the bitstream OFDM66_0_sx315.bit”.
Launch the experiment into CorteXlab
The file tree should now look like that:
... ├── fpga-pico │ ├── ofdm.py │ ├── epy_block_0.py │ ├── scenario.yaml │ ├── ofdm.grc │ └── OFDM_6_6_0_sx315.bit ...
The .grc will not be used by CorteXlab but you can let it in the same directory.
epy_block_0.py correspond to the python code of your custom block
Upload the files on airlock
Upload the fpga-pico directory on Airlock as it is explained here. For example for Linux it will look like this :
you@your-pc:~$ scp -P 2269 -r path/to/fpga-pico email@example.com:~
Create the task
Connect to Airlock through ssh as it is describe here. For example for Linux it will look like this :
you@yourpc:~$ ssh -p 2269 firstname.lastname@example.org
You can find in your home directory the fpga-pico directory that we have uploaded previously.
Now, use the Minus CLI to create the task file:
you@srvairlock:~$ minus task create fpga-pico
The success (or failure) of the creation will be printed on screen. The task file will be created at the same level and name than the targeted experiment folder but with a
.task suffix, in our case
Note: You can get help on the Minus CLI at anytime with
Book the testbed with OAR
As it is explain here, we need to book the testbed with OAR in order to run our experiment. Once you have booked the platform, you will be the exclusive user of the testbed, avoiding experimentation interference.
A basic example to submit an OAR interactive job requesting all available nodes, for 30 minutes:
$ oarsub -I -l nodes=BEST,walltime=0:30:00
Submit the task
Now, we have booked the testbed and we have a
.task file containing our experiment. In order to run it, we need to submit it to the testbed scheduler.
For now, the scheduler is a simple FIFO queue, but a more advanced scheduling mechanism will be implemented in next versions of CorteXlab.
To submit a task to the scheduler, use the Minus CLI:
you@srvairlock:~$ minus task submit fpga-pico.task
On screen will be prompted the id of your task in the scheduler. Note it down so that you can easily retrieve your results or monitor the progress of the experiment.
Observe the result
You can check the status of your experiment through the testbed scheduler. To do so, use the Minus CLI:
you@srvairlock:~$ minus testbed status Testbed status: ID count so far: 15 Number of awaiting jobs: 0 ID of the running job: None (None means server is idle)
The information given by this command enables you to deduce your experiment status. It's quite self-explanatory.
Once it's finished (we have set the duration to 5 minutes), Minus will take care of copying the results and output messages back to your home folder in srvairlock, such that you can analyse it.
All results are stored by task number in the results folder, inside your home folder.
Go in this folder :
you@srvairlock:~/fpga-pico$ cd ~/results you@srvairlock:~/results$ ls task_15 you@srvairlock:~/results$ cd task_15 you@srvairlock:~/results/task_15$ ls node31.tgz
So we see that we have a folder for each task and inside each folder one compressed file per participant node. Let's extract node31.tgz :
you@srvairlock:~/results/task_15$ tar -zxf node31.tgz you@srvairlock:~/results/task_15$ ls node31 node31.tgz you@srvairlock:~/results/task_15$ cd node31 you@srvairlock:~/results/task_15/node31$ ls epy_block_0.py impact_instr.txt ofdm.py stdout.txt epy_block_0.pyc OFDM_6_6_0_sx315.bit scenario.yaml _impactbatch.log ofdm.grc stderr.txt
We see that all of the files we used to create the task are inside. The others are:
stdout.txt: all output messages from your GNU Radio python script are written here. These include GNU Radio messages as well as all “print”s you include in your code. Seeing the contents of this file is useful to assert its correct operation.
stderr.txt: all error messages are printed here. If you see strange things on the
stdout.txtor nothing at all, it might be interesting to take a look at the
stderr.txtto debug your code.
impactinstr.txt: Impact execution log
: The impact instructions used to flash the bitstream (this file is automatically generated) *impactbatch.log
Let's take a look inside
you@srvairlock:~/results/task_15/node31$ less stdout.txt
You should get a file looking like this:
set_min_output_buffer on block 1 to 2048 nutaq_carrier_perseus_TX connected to 192.168.0.101 Registering carrier nutaq_carrier_perseus_TX Global init begin radio420_tx::InitRadio420x: 1 Powering up hardware Resetting hardware Configuring PLL - Reference frequency 30720000 Hz from internal oscillator - Acquisition frequency 76800000 Hz - Lime frequency 30720000 Hz PLL is locked Configuring Gains Automatic calibration radio420_rx::InitRadio420x: 1 Automatic calibration rx ended radio420_tx::InitRadio420x: 2 Resetting hardware Configuring PLL - Reference frequency 30720000 Hz from external connector - Acquisition frequency 76800000 Hz - Lime frequency 30720000 Hz PLL is locked Configuring Gains Automatic calibration radio420_rx::InitRadio420x: 2 Automatic calibration rx ended rtdex_sink: Init rtdex_sink: Device MAC Address: 00:d0:cc:0a:01:97 rtdex_sink: Host MAC Address: 78:d0:04:20:74:69 rtdex_source: Init rtdex_source: Device MAC Address: 00:d0:cc:0a:01:97 rtdex_source: Host MAC Address: 78:d0:04:20:74:69 Global init end successfully rtdex_sink: End Good rtdex_source: End Good ---------------------------------- 98.028 percent valid over 100000 samples ---------------------------------- rtdex_sink: stopped rtdex_source: stopped Unregistering carrier nutaq_carrier_perseus_TX
Here we have 98% valid received numbers, which is what we expected because the two antennas are close. You should have a result at least superior to 90%.
Obviously, the next step for you is to try an experiment with your own bitstream. But you can also try to adapt other Nutaq example bistream !