\part{HOWTOs}
======From Conception to Measurement======
\index{From Conception to Measurement|(}
Of course the main idea behind the MSR is that you're able to write
new modules for it. This chapter will give an introduction with an
actual example of a module as well as an implementation of a radio-transmission.
After this you should be able to create your own modules and put them
into use. An important introduction can be found in chapter
cha:simple radio.
You should also already have run the example in chapter
cha:getting started.
This part is a bit heavy on coding, but you won't be able to write
modules without a good knowledge of C.
=====Defining New Modules=====
\index{From Conception to Measurement!Defining new modules}
In here you will learn the most important things about a module, how
it works, how to use it, and how to extend it. This example is already
present in the tree, but you can read this section to get a feel of
it.
The goal of this module will be to measure the SNR of the signal.
Even though this functionality is already implemented in a module,
it is a nice idea to have a possibility to compare the results of
the two approaches. The existing module compares the received training
sequence with the original in order to calculate the SNR. As the training
sequence is only part of a transmitted slot, it is a good idea to
compare this SNR with the SNR computed in here.
digraph structs {
node [shape=record];
struct7 [label="Random | Midamble | Random";width=5];
}
The example slot|cap:The-example-slot
In order to calculate the SNR differently, we will transmit a random
sequence and then compare it after the transmission. This is depicted
in fig. cap:The-example-slot. In order to know the exact amplitude,
it is important to know the random sequence in advance. This is done
by setting the //seed// parameter of the random-module.
Once we have the received signal
y=y_{r}+iy_{i}=[y_{0r}y_{1r}...y_{n-1r}]+i[y_{0i}y_{1i}...y_{n-1i}]
and the transmitted signal
x, we can calculate the amplitude:
a=\frac{\sum_{l=0}^{n-1}y_{lr}sign(x_{lr})+y_{li}sign(x_{li})}{n}
and also the variance:
v=\frac{\sum_{l=0}^{n-1}(y_{lr}-a\, sign(x_{lr}))^{2}+(y_{li}-a\, sign(x_{li}))^{2}}{2n}
and the signal to noise ratio is then
SNR=10*\log(\frac{a^{2}}{v})
The correctness of this assertion is left as an exercise to the reader.
====The Files====
Now that we know what it is about, let's have a look at the written
files. For your information you will learn where the templates for
the files come from in every section. The discussion then is only
about the parts that have been added. In the directory //Modules/Signals//,
there is a directory called //SNR//. in there you find the code
for the SNR-module. The MSR knows about this directory because of
the file //Modules/Signals/Makefile// that has an entry //SNR//
in the list of //DIRS//. You can have a look at this Makefile to
see it.
The template files come from the //Conventions// directory and
are called:
Conventions/multi_*.c
Conventions/Makefile.module
Most of the modules come in two parts: one sending part and one receiving
part. But as they usually are used in a pair, they are put together
into one module. So as not to be confused with different modules,
they are renamed to:
multi_template.c -> snr.c
multi_template_send.c -> snr_send.c
multi_template_rcv.c -> snr_rcv.c
Makefile.module -> Makefile
===snr.c===
Now look first at the file //snr.c// and look at the places that
contain some documentation. It is really important to keep this documentation
up-to-date, so that other people know what it's about:
SNR - measure the signal to noise ratio of a transmitted slot.
In order to do this, we send a slot of known random data that
is measured on the other side.
This is all that is needed. Not a big description, just enough to
know what it's about.
What it does is the following: once the module //snr// is loaded
into the memory, the function //spc_module_init// is called,
which in turn calls //rcv_module_init// from //snr_rcv.c//
and //send_module_init// from //snr_send.c//. These two functions
are responsible to tell the CDB about their name, their input and
output as well as their paramters.
===snr_send.c===
This is the part that prepares the slot.
**documentation. **
At the top of the file, you see again a short description of the module
snr_send.c - sends a slot of random symbols
and a bit further down (after the copyright message) a bit more of
description.
This module expects some random input that is then modulated
using QPSK modulation so that the noise can be mesured. It can
send the QPSK symbols either on the axis or in the corners.
**config-structure**
\index{Module!Example!config}
We want the user to be able to change the amplitude of what we send
over the channel. So, edit the config-structure, and make it something
like:
typedef struct {
// The amplitude of the generated QPSK-signal
int amplitude; // 32767
// The QPSK-type, 0->in the corner, 1->on the axes
int type; // 0
} config_t;
It may seem strange that we take an integer for the amplitude, but
you have to know that the signals are all in 16-bit integers, so what
usually is between
+1
and
-1
is now between
+32767
and
-32768.
**private-structure**
\index{Module!Example!private}
Once the user changed the configuration, we will store it in our private
variable. This is more for convenience than anything else:
typedef struct {
int amplitude;
int type;
} private_t;
The other structure may be left empty, we don't need it for this example.
Just after the structures is a function called
**send_init**
\index{Module!Example!init}
Why another initialisation function, you might ask. Well, remember
from chapter sec:framework that first the module is registered
with the CDB, before it is possible to instantiate it. So, this function
is what is called each time this module is instantiated. In our example,
we just want to put a default-value in the amplitude-part of the configuration,
so add this line:
config->amplitude = 32767;
config->type = 0;
32767 is the maximum number that we can have in a 16-bit signed variable.
**send_configure_input**
\index{Module!Example!configure_input}
This function is called whenever the MSR wants to know what the size
of the input should be, given the size of the output. So, for each
2 bit of input, we create one symbol with the QPSK representation.
This means that two bits of input create one symbol of output, at
least for an even number of symbol-outputs. The only tricky part here
is that the input is not counted in bits, but rather in bytes. So
size_{input}=\frac{2*size_{output}}{8}, which is written in this
function as
size_in(0) = ( size_out(0) + 7 ) * 2 / 8;
This assures that we always have enough bits to write to the output.
**send_configure_output**
\index{Module!Example!configure_output}
The same as before, but this time the opposite direction:
size_out(0) = size_in(0) * 8 / 2;
**send_reconfig**
\index{Module!Example!reconfig}
We use this function to copy the configuration-data to our private
structure:
private->amplitude = config->amplitude;
private->type = config->type;
**send_pdata**
\index{Module!Example!send_pdata}
Now comes finally the processing function. This is where the main
action takes place. Let's first define some variables:
// Definition of variables - don't touch
stats_t *stats;
int i, amp;
U8 *in;
SYMBOL_COMPLEX *out;
To get to the input and output-buffers, we have to do the following:
in = buffer_in(0);
out = buffer_out(0);
The first line deletes the //data// bit on the input-port, signaling
the MSR that the input-port is free again to receive some data. Furthermore
it returns a pointer to the input-buffer of this module. The second
line gets the output-buffer of this module and sets the //data//
bit on the output-port. Once this function returns, the MSR checks
for the //data//-bit in the output-port and, if it is set, handles
the processing to the next module.
OK, now we just have to process the data:
// Fill the slot with random QPSK symbols
for ( i=0; iamplitude / sqrt( 2 );
out[i].real = ( 2 * ( *in & 1 ) - 1 ) * amp;
*in = *in >> 1;
out[i].imag = ( 2 * ( *in & 1 ) - 1 ) * amp;
*in = *in >> 1;
break;
case 2:
amp = private->amplitude;
if ( *in & 1 ){
*in = *in >> 1;
out[i].real = ( 2 * ( *in & 1 ) - 1 ) * amp;
out[i].imag = 0;
} else {
*in = *in >> 1;
out[i].imag = ( 2 * ( *in & 1 ) - 1 ) * amp;
out[i].real = 0;
}
*in = *in >> 1;
break;
}
// Get the next input-byte of random
if ( i && !( i % 4 ) ){
in++;
}
}
You might not be completely fond of this example, but it works (yet
to check).
**send_custom_message**
This function is not needed and can be deleted
**send_module_init**
\index{Module!Example!module_init}
Registers this module with the CDB. The CDB first wants to be informed
about the type of module to be attached
((For further information, look at subsec:cdb
)). In our case, we have one input, one output, one config-parameter
and 0 stats-parameter:
desc = swr_spc_get_new_desc( 1, 1, 2, 0 );
Then we have to tell about the config-parameter, the input-type and
the output-type:
UM_CONFIG_INT( ''amplitude'' );
UM_CONFIG_INT( ''type'' );
UM_INPUT( SIG_U8, 0 );
UM_OUTPUT( SIG_SYMBOL_COMPLEX, 0 );
In order for the MSR to know what functions to call in what case,
we have to define 'call-back functions'. As these are always the same,
they are already pre-defined in the templates, and we only have to
delete the //send_custom_msg// entry. Now the module-description
is complete, save for the name:
send_id = swr_cdb_register_spc( &desc, "snr_send" );
===snr_rcv.c===
Let's start with the comment in the beginning of the file:
This module receives the stream from the matched filter
and the stream of random-signals that are supposed to be
the same that have been used by the snr_send. It then
calculates the amplitude, the variance and the snr.
This means that this module has two inputs: one from the channel,
and another one from the random-module.
**config-structure**
\index{Module!Example!config}
Again we have the possibility to change the type:
typedef struct {
// The QPSK-type, 0->in the corner, 1->on the axes
int type;
} config_t;
**stats-structure**
\index{Module!Example!stats}
So we're able to retrieve the SNR from the outside, we have to write
it in this structure:
typedef struct {
double snr;
} stats_t;
**rcv_init**
\index{Module!Example!init}
Let's just start with a SNR of -2.3:
stats->snr = -2.3;
config->type = 0;
**rcv_reconfigure**
\index{Module!Example!reconfigure}
private->type = config->type;
The functions //rcv_configure_input// and //rcv_configure_output//
can be deleted, as this module is at the end of the chain.
**rcv_pdata**
\index{Module!Example!pdata}
Here goes the working function. It's just about implementing the above
formula. Let's go through step by step. Definition of variables:
stats_t *stats;
SYMBOL_COMPLEX *in, *buf_rnd;
U8 *in_rnd;
double signal = 0., noise = 0.;
int i;
Then we have to make sure that we have both the signal from the channel
and the random-data:
if ( !data_available(0) !data_available(1) ){
PR_DBG( 4, "Not all data available yet\n" );
return 0;
}
Now we reconstruct
x
so that we can implement the formula given
above. Instead of calculating
x
and then taking the sign of it,
we directly calculate
x
with an amplitude of
\pm1. Again, once
for the QPSK-signals on the axes, and once for the signals tilted
by
\frac{\pi}{4}
in_rnd = buffer_in(1);
buf_rnd = swr_malloc( size_in(0) * sizeof( SYMBOL_COMPLEX ) );
for ( i=0; itype ){
case 0:
buf_rnd[i].real = ( 2 * ( *in_rnd & 1 ) - 1 );
*in_rnd = *in_rnd >> 1;
buf_rnd[i].imag = ( 2 * ( *in_rnd & 1 ) - 1 );
*in_rnd = *in_rnd >> 1;
break;
case 1:
if ( *in_rnd & 1 ){
*in_rnd = *in_rnd >> 1;
buf_rnd[i].real = ( 2 * ( *in_rnd & 1 ) - 1 );
buf_rnd[i].imag = 0;
} else {
*in_rnd = *in_rnd >> 1;
buf_rnd[i].imag = ( 2 * ( *in_rnd & 1 ) - 1 );
buf_rnd[i].real = 0;
}
*in_rnd = *in_rnd >> 1;
break;
}
// Get the next input-byte of random
if ( i && !( i % 4 ) ){
in_rnd++;
}
}
Now we can calculate the amplitude of the signal:
in = buffer_in(0);
// Calculate signal energy
for ( i=0; i
And the noise-variance:
for ( i=0; i
Finally we can calculate the snr:
PR_DBG( 2, "signal_amp: %i, noise_amp: %g\n",
(int)signal, noise );
// And write the snr
swr_sdb_get_stats_struct( context->id, (void**)&stats );
if ( noise > 0 ){
stats->snr = 10 * ( log10( signal ) * 2 - log10( noise ) );
}
swr_free( buf_rnd );
Again, the //rcv_user_msg// is not used and can be deleted.
**rcv_module_init**
We have only 1 input, no output, 1 config-variable and 1 stats-variable,
and lots of functions are not used:
desc = swr_spc_get_new_desc( 1, 0, 1, 1 );
UM_STATS_DOUBLE( ''snr'' );
UM_INPUT( SIG_SYMBOL_COMPLEX, 0 );
desc->fn_init = rcv_init;
desc->fn_reconfigure = rcv_reconfig;
desc->fn_process_data = rcv_pdata;
desc->fn_finalize = rcv_finalize;
rcv_id = swr_cdb_register_spc( &desc, "snr_rcv" );
===Makefile===
\index{Module!Example!Makefile}
In the makefile we have to tell the final name of the module, as well
that we use the math-library:
MODULE_NAME = snr
MATH = true
====Compile it====
Now you can try to compile it by typing //make// on the command-line.
If there are any errors, try to fix them, the above lines should work,
they have been tested. In order to include this module even better
in the MSR, you can add the name of the directory to the file //Modules/Signals/Makefile//
in the line //DIRS// = . Like this a top-level //make// will
also update the SNR-module.
=====Testing=====
Up to now only the module has been written. It is not yet in a usable
state, as it is only registered with the CDB, but not yet instantiated.
Theoretically we could write everything in the module to make an instance,
but this would turn upside-down the idea of modules. So what we need
is an own program that implements the chain and runs it, just to look
how good it runs.
Perhaps as a surprise, this program will again be a module, but this
time a module that does actually something. Implementing a simple
chain. So there is a function called //um_module_init// that
will be called upon inserting the module. This function itself creates
a new thread that will be used to create the chain. In order to be
compatible for further RTLinux implementation, we have to do this
two-step calling.
====The Directory====
In the MSR, there is a directory called //Test// which holds already
different tests. The test for the SNR is of course in a directory
called //Test/SNR//. The templates for the test-module are in
Conventions/test_template.c
Conventions/Makefile.module
Again, for easier handling they are renamed:
test_template.c -> test_snr.c
Makefile.module -> Makefile
====Makefile====
The makefile wants to know the name of the module, which is //test_snr//,
as well as the modules to load in order for the MSR to function correctly.
In our case, these are the modules //random, snr, midamble, rrc//
and //block//:
MODULE_NAME = test_snr
DEPENDS = random snr chest rrc block
====test_snr.c====
Let's have a look at the documentation:
Make a simple chain:
random - snr_send - chest_send - rrc - block -
rrc_rcv - chest_rcv - snr_rcv
and additionally:
random - snr_rcv(2)
Then we can create our main-function, which is called //start_it//
for the test-program.
===start_it===
The first thing we have to do is to create a //chain// of modules.
A chain is a logical suit of signal-processing modules, that take
some input and produce some output that is handled further down the
chain.
When using the //swr_chain_create// functionwe give a list of
all modules, that will be automatically connected together, and finish
the list with END_CHAIN. In this call to //swr_chain_create//,
you see three different kind of macros, //NEW_SPC_VAR//, //NEW_SPC//
and OLD_SPC_IN all of which are described in subsec:sdb.
In short, while the former allows you to give a variable where a reference
to the module will be stored, the latter just creates the module and
connects it, without giving the reference of the created module. The
third takes an already defined module for further connections.
swr_sdb_id rnd, mafi, snr_rcv;
test_chain = swr_chain_create(
NEW_SPC_VAR( ''random'', rnd ),
NEW_SPC_VAR( ''snr_send'' ),
NEW_SPC( ''chest_send'' ),
NEW_SPC( ''rrc'' ),
NEW_SPC_VAR( ''block'' ),
NEW_SPC_VAR( ''rrc_rcv'', mafi ),
NEW_SPC_VAR( ''chest_rcv'', mafi ),
NEW_SPC_VAR( ''snr_rcv'', snr_rcv ),
END_CHAIN );
swr_sdb_set_config_int( mafi, "cacl_taps", 8 );
test_chain_2 = swr_chain_create(
NEW_SPC_VAR( ''random'', rnd2 ),
OLD_SPC_IN( snr_rcv, 1 ),
END_CHAIN );
So we have created a chain. The modules have the following function:
randomcreate random bytessnr_sendour module created above, takes some random bytes as input, and creates a QPSK outputchest_sendinserts the training-sequence in the middle of the streamrrc Root Raised Cosine pulse-shape filteringblocka very simple channel-simulationrrc_rcv Applies again the Root Raised Cosine filteringchest_rcvuses the training-sequence to make a channel-estimation and does a matched-filtering on the received samplessnr_rcvour module to calculate the SNR
If we compile and test our module with //make; make user//, this
chain will be created and the program will exit. What we forgot is
to really use this chain. For this, the //random// module listens
to user-messages, and begins creating a random-output whenever it
receives such a user-message. But first we have to make sure that
both random-modules create the same values:
swr_sdb_set_config_int( rnd, "seed", 0x1234 );
swr_sdb_set_config_int( rnd2, "seed", 0x1234 );
Then we adjust a bit the amplitudes, so that we don't run all the
time on the edge:
swr_sdb_set_config_int( snr_send, "amplitude", 16384 / 4 );
swr_sdb_set_config_int( mid, "amplitude", 16384 / 4 );
To make it a bit more nice, we have a look at different values, using
the //sigma// parameter of the //block// module:
for ( i=0; i<50; i+=5 ){
swr_sdb_set_config_double( block, "sigma", i );
swr_sdb_send_msg( rnd, SUBS_MSG_USER, NULL, -1 );
swr_sdb_send_msg( rnd2, SUBS_MSG_USER, NULL, -1 );
PR( "Amp: %2i:%2i, Noise: %3i:%3i SNR : %5.5g - %5.5g = %5.5g\n",
swr_sdb_get_stats_int( mafi, "mid_amp" ),
swr_sdb_get_stats_int( snr_rcv, "amp" ),
swr_sdb_get_stats_int( mafi, "noise_var" ),
swr_sdb_get_stats_int( snr_rcv, "var" ),
swr_sdb_get_stats_double( mafi, "snr" ),
swr_sdb_get_stats_double( snr_rcv, "snr" ),
swr_sdb_get_stats_double( mafi, "snr" ) -
swr_sdb_get_stats_double( snr_rcv, "snr" ) );
}
And after a //make; make user// you should see something like:
Amp: 63:63, Noise: 1:1 SNR : 34.40 - 34.24 = 0.15426
Amp: 62:62, Noise: 5:6 SNR : 28.52 - 28.10 = 0.42007
Amp: 62:62, Noise: 27:26 SNR : 21.50 - 21.58 = -0.077267
Amp: 62:62, Noise: 66:63 SNR : 17.61 - 17.83 = -0.21601
Amp: 62:61, Noise: 122:111 SNR : 14.96 - 15.34 = -0.37944
Amp: 61:61, Noise: 190:187 SNR : 12.89 - 13.07 = -0.18071
Amp: 58:59, Noise: 179:219 SNR : 12.72 - 12.01 = 0.70692
Amp: 65:64, Noise: 317:322 SNR : 11.23 - 11.06 = 0.16854
Amp: 66:63, Noise: 421:393 SNR : 10.14 - 10.15 = -0.016712
Amp: 65:65, Noise: 528:555 SNR : 9.026 - 8.83 = 0.18867
So you see that our method gives more or less the same results as
the //snr// calculated in the //matched_filter//.
=====Going Over the Air=====
OK, now that the module is written, a simple test-case shows that
our module works, we can go on and write a simple radio that
transmits the SNR-slot and then receives it and shows the result.
We will make a simple radio that has a master, the BaseStation, that
transmits the synchronisation-signal, and a client, the MobileStation,
that synchronises to it and sends a SNR-slot in return.
====The Directories and Files====
It will be a radio, so we find the code in the directory //Radios/SNR//.
The base for this radio is the simple-radio that can be found at //Radios/Simple//
and contains the following files:
simple_radio.h
Makefile
BS/Makefile
BS/radio_bs.c
MS/Makefile
MS/radio_ms.c
To reflect the fact that it isn't the //simple// radio anymore,
we have to adjust the names in the Makefiles. The first lines in //BS/Makefile//
contains:
MODULE_NAME = radio_snr_bs
DEPENDS = rrc synch energy mapper chest random \
rndstr spread sink cch macro_sch snr
and in //MS/Makefile// reads:
MODULE_NAME = radio_snr_ms
DEPENDS = rrc synch energy mapper chest random \
rndstr spread sink macro_synch macro_sch snr
====README====
This file also reflects the changes and has a very small documentation
in it.
====MS/radio_ms.c====
There is a lot of things to say about the basic system. You can find
an introduction in cha:simple radio. Here we just try to
focus on the things necessary to run our SNR-module over a real channel.
A very short simplification: when the mobile synchronises for the
first time to the base-station, it creates the necessary chains. This
happens in the function //synchronised//. Near the end of this
function, you have to replace the declaration of the UP-chain with
the following chain:
// And setup a simple UP-chain...
ch_up = swr_chain_create(
NEW_SPC_VAR( "random", rnd ),
NEW_SPC( "snr_send" ),
NEW_SPC( "chest_send" ),
NEW_SPC( "rrc" ),
OLD_SPC_IN( stfa, 1 ),
CHAIN_END );
swr_stfa_notice_sdb( stfa, 1, rnd );
PR( "Ready to go" );
You see a new macro in the function //swr_chain_create// here,
which is called //OLD_SPC_IN// and uses the //stfa// module.
The macro tells the function that this module has already been created
before. The module //stfa// is quite important in the MSR: it creates
the link between the modules and the actual hardware. So an input
to the stfa is like a connection to the antenna.
As we don't know exactly when the mobile will be synchronised with
the base-station, we set the seed of the //random// module every
frame to the same value. Like this we're sure that both the BS and
the MS have the same random-values. To achieve this, the function
//do_send_up// is handy. It is called once in a frame, and we
can put the following line in there:
swr_sdb_set_config_int( rnd, "seed", 0x1234 );
That's it for the mobile-station part.
====radio_bs.c====
This part of the radio sets up the chains for reception anyway and
then just waits on what happens. As it gives the synchronisation and
doesn't need to wait for it, it is much more simple than //radio_ms.c//.
So we can directly change the construction of the UP-chain in the
function //start_it// to:
PR( "Setting uplink in slot 1\n" );
ch_rach = swr_chain_create(
OLD_SPC_OUT( stfa, 1 ),
NEW_SPC( "rrc_rcv" ),
NEW_SPC_VAR( "chest_rcv", mafi ),
NEW_SPC( "snr_rcv" ),
CHAIN_END );
ch_rach_2 = swr_chain_create( NEW_SPC_VAR( "random", rnd ),
OLD_SPC_IN( snr_rcv, 1 ),
CHAIN_END );
swr_sdb_set_config_int( sch, "mafi0", mafi );
while ( looping++ ){
usleep( 1000000 );
PR( "mafi0: %g, mafi1: %g\n",
swr_sdb_get_stats_double( mafi, "snr" ),
swr_sdb_get_stats_double( snr_rcv, "snr" ) );
}
One last important thing we don't have to forget: once a frame we
have to tell the random-module to generate some data. This is best
done in the function called //do_send_down//, which is used once
a slot. So we can insert there:
swr_sdb_set_config_int( rnd, "seed", 0x1234 );
swr_sdb_send_msg( rnd, SUBS_MSG_USER, NULL, -1 );
====Running it with the channel-simulation====
Again, to help track down errors, it is more adviseable to run it
first in simulation-mode, like this you can track down errors much
more simple. In order to do so, change to the directory //Radios/SNR//
and type //make// to compile it, and //make server; make show_bsms//
which should bring up two windows, one showing the mobilestation and
another showing the basestation. If something goes wrong with the
compilation, fix it and run //make// again. If something goes wrong
with the display, type //make kill; make cleanproc// which should
clean-up the directories, and then you can try //make server;
make show_bsms// again.
====Running the real thing====
Now that you did all this work and the modules returned some decent
values, you can be pretty sure that it shouldn't run havoc in real-time
mode. So let's try it. First, you have to run the radio on the basestation,
issuing a //make rf_show// from the //Radios/SNR/BS// directory.
Then, on the other machine, you can run //make rf_show// from
the //Radios/SNR/MS// directory. If everything is set up correctly,
the hardware is OK and all things are nicely connected and plugged
in (this will give another chapter or two, installing the hardware),
you should again see two windows, one from the basestation and one
from the mobilestation, and the values this time are REAL values.
If you come this far, congratulations!
\index{From Conception to Measurement|)}