======Creating a simple radio======
In this chapter you'll learn how to create a very simple radio consisting of a
transmitter and a receiver. You should know the very basics of a digital
transmission. For a short introduction in this matter, you can also go and read
chapter cha:signal flow.
In figure fig:first_radio you see the goal of this tutorial: two radios,
called //Master// and //Client// with the following functions:
* Master: send the synchronisation, training-sequence and an image
* Client: synchronize, do a channel estimation and decode the image
digraph structs {
node [shape=record];
struct1 [label="image_send", width=1.3, pos="0,0"];
struct2 [label="modulator", width=1.3, pos="0,1"];
struct3 [label="spreadind", width=1.3, pos="0,1.5"];
struct4 [label="chest_send";width=1.3];
struct5 [label="sync_send";width=1.3];
struct6 [label="rrc";width=1.3];
struct7 [label=" Slot 0 | Slot 1 | Slot 2";width=3.9];
struct1 -> struct2;
struct2 -> struct3;
struct3 -> struct4;
struct4 -> struct5;
struct5 -> struct6;
struct6 -> struct7:s0;
stfa [label="STFA", shape="plaintext"];
struct21 [label=" Slot 0 | Slot 1 | Slot 2", width="3.9", rect="5,10,8.9,10.3"];
struct22 [label="sync_rsv", width=1.3, pos="0,1"];
struct23 [label="rrc_rsv", width=1.3, pos="0,1.5"];
struct24 [label="chest_rsv";width=1.3];
struct25 [label="despread";width=1.3];
struct26 [label="slicer";width=1.3];
struct27 [label="image_rsv", width=1.3, pos="0,0"];
struct21:s0 -> struct22;
struct22 -> struct23;
struct23 -> struct24;
struct24 -> struct25;
struct25 -> struct26;
struct26 -> struct27;
{ rank = same; struct7; struct21; stfa; }
}
A very simple radio|fig:first_radio
Both the master and the client can be represented by a simple chain. So it will
be very easy to implement these two radios as shown in the rest of this
tutorial.
After explaining some more general things about the software-radio, we'll
explain first the master, then the client. You can find the basic files with
all the basic setup already done in the sub-directory //Radios/ICS_Simple//
of the SRadio-project. //Master/radio_master.c// contais the basic setup of
the master and //Client/radio_client.c// the basic setup of the client.
=====General Setup=====
This section is just a very brief introduction to the software-radio. For a
more complete overview, follow the references. First some overview of the
software-radio, then an explanation of the C-files.
====Overview====
As explained in chapter cha:modes of operation, the software-radio can be
run either in simulation- or in real-time-mode. While it is possible to have
both the master and the client on the same computer in simulation mode, this is
not possible in real-time mode. The first part of this HOWTO works only in
simulation-mode, so you need neither hardware nor two computers to run the
examples.
In simulation mode, the master and the client connect to a
//channel-server// which simulates a simple wireless connection with some
noise and a fading-channel. Both the master and the client run independently of
each other and the only connection is the channel-server. Each one has a
complete software-radio environment with a CDB and a SDB (see also
subsec:cdb and sdb), so that they can communicate only through the
channel-server. This corresponds to the real transmission where the two
radios can only communicate through the channel.
====Files====
Because the master and the client are independent entities, they are put in
a seperate directory. In each directory a small template-file can be found
that contains the initialisations necessary for the software-radio. For this
tutorial, two functions are important:
* start_it comparable to the //main//-function in C-programs or the //constructor// in a C++-class
* um_module_exit which corresponds to the //destructor//-function in a C++-class.
In the start_it-function the radio initialises some modules and/or
chains (see subsec:modules and chains) which are cleaned-up in
the um_module_exit-function.
=====Master=====
The master needs to send an image, encode it using a spreading-sequence,
add a trainig-sequence for the channel estimation and put a
synchronisation-signal on top of this, so that the client can synchronise.
Looking at figure fig:first_radio, we can directly deduce the necessary
chain:
digraph DokuWikiParser {
node [shape=box];
rankdir=LR
image_send -> modulator
modulator -> spread
spread -> chest_send
chest_send -> synch_send
synch_send -> rrc
}
The most simplest way to do that is to create a chain with all the modules
inside. In order to do that, open the file for the master in
//Radios/ICS_Simple/Master/radio_master.c// and put the following text in
the function //start_it//:
swr_sdb_id img, spread;
PR( "Setting up send-chain\n" );
send_ch = swr_chain_create(
NEW_SPC_VAR( "image_send", img ),
NEW_SPC( "mapper" ),
NEW_SPC_VAR( "spread", spread ),
NEW_SPC( "chest_send" ),
NEW_SPC( "synch_send" ),
NEW_SPC( "rrc_complex_send" ),
NEW_SPC_VAR( "stfa_ics", stfa ),
CHAIN_END );
For more explanation on the NEW_* commands, see subsub:swr_chain_create.
In short we use //NEW_SPC// to instantiate a new module and
//NEW_SPC_VAR// to instantiate a module and get a reference-id back.
Now that this chain is created, we need to tell the STFA((read
subsec:stfa for more information)) that this is a transmission-chain. We
use the following command:
swr_stfa_notice_sdb( stfa, 0, img );
Now everytime the STFA wants to send slot 0, it will call the
img-instantiation of the "image_send"-module. Finally we set up the STFA in
transmission-mode and tell the spread-module to use a spreading-factor of
two((which is equivalent to a simple repetition code)):
swr_sdb_set_config_int( stfa, "tx", 1 );
swr_sdb_set_config_int( spread, "factor", 2 );
Now we're ready to start the STFA:
swr_stfa_go( stfa );
That's it for the start_it function. Because we use the //send_ch// and
the //stfa// globally, we need to define these above the start_it-function
like this:
struct chain_t *send_ch;
swr_sdb_id stfa;
And, last but not least, we need to clean up the chain in case the radio gets
stopped. This is done by inserting the following lines at the end of the
//um_module_exit//-function:
if ( stfa ){
PR( "Stopping the STFA$\backslash$n" );
swr_stfa_stop( stfa );
PR( "Deleting Sending-chain$\backslash$n" );
swr_chain_destroy( send );
}
====Testing the Master====
Now that the master is written, we can test it. First of all, make sure that
you can compile it, using
make
and that there are neither warnings nor errors. Then you can call
make show_local
and if everything is OK, you should see a window coming up that displays the
STFA as a horizontal white bar and the chain attached to it. Right-clicking on
the //image_send//-module you can chose //Display Data// and then
//picture// which will pop up a window with the picture sent in it.
Keeping this window and right-clicking on //rrc_complex_send//, then
//Display outputs// and //port_out_0// shows the output of this chain
in complex format. In this new window you can click on //Complex// and chose
//FFT// to see a fast-fourier transform of the same output.
You can also change the behaviour of some of the modules. For example you can
change the //factor//((this is the spreading-factor or the
spreading-length of the code)) of the //spread//-module. Right-click on the
//spread//-module and chose //Change config//. Now you can increase or
decrease the //factor//-value. If you still have the image-window open, you
can see how the image changes when increasing or decreasing the
//factor//-value. The next subsection explains why this happens.
====Slots and Blocks in the Software-Radio====
This is a small excurse in the internals of the software-radio. It's goal is to
give you some more understanding of how our implementation works. It is not
elemental but quite useful to understand the rest of the software-radio.
The software-radio works with fixed blocks of data. Everything that goes over
the air has to fit into one slot. In the current implementation, a slot is
of length 2560 symbols((this is an artefact of the first
UMTS-implementation. You can change the slot-length in multiples of 128
symbols)). Every time the software-radio wants to send a slot, the output of the
chain has to fit in 2560 symbols.
Starting from the STFA, the software-radio calculates the maximum size
available by asking each module how much symbols it needs:
|module|use|symbols|left|
|stfa|guard-period|90|2470|
|synch_send|synchronisation|256|2214|
|chest_send|training-sequence|136|2078|
|spread|spreading-sequence|\frac{1}{n}|\frac{2078}{n}|
|mapper|bit-to-symbols|*2|\frac{4156}{n}bits|
This means that the image_send-module has a variable-sized output that
can vary from 4156 to
\frac{4156}{32}=129
bits((The spreading-module
has a maximum spreading-length of 32)), depending on the spreading-factor chosen.
Every time the output-size of the image_send-module changes, it adjusts the
image-size so that it fits in the given place.
For this reason you see the image changing when you change the spreading-factor
of the spreading-module.
=====Client=====
Looking at figure fig:first_radio we can see that we have to implement
the following chain:
digraph DokuWikiParser {
node [shape=box];
rankdir=LR
stfa -> synch_rcv
synch_rcv -> rrc_rcv
rrc_rcv -> chest_rcv
chest_rcv -> despread
despread -> slicer
slicer -> image_rcv
}
There are two small subtelties when implementing this receiver-chain: first, we
put the //synch_rcv//-module in front of the //rrc_rcv//-module.
Looking at the master, we'd expect to first do the rrc-reception and then the
synchronisation. But as the rrc_rcv-module downsamples by a factor of two, it
is better to do the synchronisation at the higher sample-rate, in order to have
a more exact synchronisation.
Second, the actual module-names are quite long: sync_rcv is called
//synch_complex_ics_rcv// and rrc_rcv is called
//rrc_complex_ics_rcv//. This is due to the development-process of the
software-radio and the wish to keep old things running.
Besides these small details, the client is built more or less in the same way
as the server. All these lines go in
//Radios/ICS_Simple/Client/radio_client.c//. Before the
//start_it//-function, we declare the //stfa// and the //rcv_ch//:
swr_sdb_id stfa;
struct chain_t *rcv_ch;
In the //start_it//-function, we declare the needed variables and the
reception-chain:
swr_sdb_id synch, mafi, despread, slicer;
rcv_ch = swr_chain_create( NEW_SPC_VAR( "stfa_ics", stfa ),
NEW_SPC_VAR( "synch_complex_ics_rcv", synch ),
NEW_SPC( "rrc_complex_ics_rcv" ),
NEW_SPC_VAR( "chest_rcv", mafi ),
NEW_SPC_VAR( "despread", despread ),
NEW_SPC_VAR( "slicer", slicer ),
NEW_SPC( "image_rcv" ),
CHAIN_END );
This chain is the same as in figure fig:first_radio and described above.
Some of the modules need some configuration so that the radio works correctly.
We have to do these things:
* Gain: adjust the gain of the RF-cards, so that the cards don't saturate
* Synchronisation: tell the module the id of the STFA so it can adjust for the desynchronisation
* Despreading: set the despreading-factor to 2
* Matched Filter: calculate 8 taps and align them
* Slicer: set the id of the matched filter so that it can know the amplitude of the signal
All these values are in the configurable part of the modules, so we can set
them using the swr_sdb_set_config*-functions:
swr_sdb_set_config_int( stfa, "attn_tx", 31 );
swr_sdb_set_config_int( stfa, "attn_rx", 15 );
swr_sdb_set_config_int( synch, "stfa_id", stfa );
swr_sdb_set_config_int( despread, "factor00", 2 );
swr_sdb_set_config_int( mafi, "calc_taps", 8 );
swr_sdb_set_config_int( mafi, "align", 1 );
swr_sdb_set_config_int( slicer, "mafi_id", mafi );
Now everything is ready to start the STFA:
swr_stfa_go( stfa );
We need to do some initialisation, just in case something will go wrong. Insert
this line in the beginning of the function //um_module_init//:
stfa = 0;
And at the end we need to clean things up, so insert these lines at the end of
//um_module_exit//:
if ( stfa ){
PR( "Stopping stfa$\backslash$n" );
swr_stfa_stop( stfa );
PR( "Destroying rcv-chain$\backslash$n" );
swr_chain_destroy( rcv_ch );
}
====Testing the Client====
Compile it with //make// and correct eventual errors. Once everything
compiles nicely, you can go in the directory //Radios/ICS_Simple// and type
make show_mc
This will start the master, then the client and will bring up the
visualize-tool so that you can see the modules in action. Once the
visualize-tool is started, you can see that there are now two tabs, one for the
master and the other for the client. Clicking on these tabs you can switch
between the two radios.
====Testing the transmission====
If everything up to here works as described, congratulations. You just finished
your first very simple radio-transmission using the software-radio. While
running the master and the client, you can now visualize different modules and
change the configuration-parameters, in order to see what happens.
====Synchronisation====
A small word on synchronisation: the implementation we did in this tutorial is
the most simplest possibility. Every frame the synchronisation-module will
search for a synchronisation-signal. If it decides that there is no
synchronisation, it shifts half a slot and will try again in the next frame.
The worst-case scenario is that the synchronisation-signal is just on the
opposite side of the searching-direction, which will make the
synchronisation-module searching the whole frame. In real time, one frame
corresponds to 30ms and holds 15 slots, so that it will be searched completely
in 1sec.
For a radio-application, 1 second is quite a long time. For this reason, a
second method exists which is a bit more complicated but finds the
synchronisation-signal on the first go. A //macro-module// called
//macro_synch_ics// attaches a synchronisation-module to each slot of the
stfa. Once a whole frame is received, it loops over all synchronisation-modules
and choses the one with the strongest synchronisation-signal, discarding all
others.
This allows the software-radio to synchronise in much more efficient way. As
this method includes callback-functions and handling the macro-module, we
decided to go the easy way and just scan the whole frame.
=====RF-transmission=====
Now everything should be ready to be able to transmit over the air. Once the
simulation works, there are usually no big bugs left that can hinder the test
over the air.
As you will have to transmit from one computer to the other, the code needs to
be installed on two computers where each computer has the appropriate hardware
installed. Once this is done, it is usually more efficient to work on one
computer only and to //ssh// in the other computer and run the
software-radio remotely. This gives you the advantage of having all the output
on one screen.
To start the master, simply go into the
//Radios/ICS_Simple/Master//-directory and call
make rf_show
which should start the real-time part of the software-radio and show up the
transmission-window. On the other computer, go into the
//Radios/ICS_Simple/Client// directory and issue the same command:
make rf_show
If all goes well, you should have now two windows where one shows the master
and the other the client. You can now play around with the values to see how
the software-radio reacts. Interesting configuration-parameters include the
attn_tx on the master-side, the attn_rx on the client side. Both can be
found in the stfa, the horizontal white line in the middle of the window.
Other things to experiment with is the calc_taps of the chest_rcv-module or
the factor and sequence configuration of the spread and despread-module.
====Going Further====
Now that you have this simple radio running, it is possible to change the
spreading-module with something else, for example a convolution-module or even
an ldpc-module. You can also add other chains to the stfa in a similar manner
than the ones we did. If you do so, don't forget that you don't need a
synchronisation-module anymore on the additional chains. One
synchronisation-module per frame is enough.