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 . You should also already have run the example in chapter . This part is a bit heavy on coding, but you won't be able to write modules without a good knowledge of C.
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.
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. 24.1. 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 and the transmitted signal , we can calculate the amplitude:
and also the variance:
and the signal to noise ratio is then
The correctness of this assertion is left as an exercise to the reader.
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/Makefile.module
multi_template_send.c -> snr_send.c
multi_template_rcv.c -> snr_rcv.c
Makefile.module -> Makefile
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:
In order to do this, we send a slot of known random data that
is measured on the other side.
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.
This is the part that prepares the slot.
At the top of the file, you see again a short description of the module
using QPSK modulation so that the noise can be mesured. It can
send the QPSK symbols either on the axis or in the corners.
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:
// The amplitude of the generated QPSK-signal
int amplitude;
// The QPSK-type, 0->in the corner, 1->on the axes
int type;
} config_t;
Once the user changed the configuration, we will store it in our private variable. This is more for convenience than anything else:
int amplitude;
int type;
} private_t;
Why another initialisation function, you might ask. Well, remember from chapter 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->type = 0;
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 , which is written in this function as
The same as before, but this time the opposite direction:
We use this function to copy the configuration-data to our private structure:
private->type = config->type;
Now comes finally the processing function. This is where the main action takes place. Let's first define some variables:
stats_t *stats;
int i, amp;
U8 *in;
SYMBOL_COMPLEX *out;
out = buffer_out(0);
OK, now we just have to process the data:
for ( i=0; i<size_out(0); i++ ){
switch( 2 ){
case 1:
// The amplitude in this case is
// Sqrt( Re^2 + Im^2 ) and thus the
// desired amplitude has to be divided by
// sqrt(2)
amp = private->amplitude / 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++;
}
}
This function is not needed and can be deleted
Registers this module with the CDB. The CDB first wants to be informed about the type of module to be attached24.1. In our case, we have one input, one output, one config-parameter and 0 stats-parameter:
UM_CONFIG_INT( ''type'' );
UM_INPUT( SIG_U8, 0 );
UM_OUTPUT( SIG_SYMBOL_COMPLEX, 0 );
Let's start with the comment in the beginning of the file:
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.
Again we have the possibility to change the type:
// The QPSK-type, 0->in the corner, 1->on the axes
int type;
} config_t;
So we're able to retrieve the SNR from the outside, we have to write it in this structure:
double snr;
} stats_t;
Let's just start with a SNR of -2.3:
config->type = 0;
Here goes the working function. It's just about implementing the above formula. Let's go through step by step. Definition of variables:
SYMBOL_COMPLEX *in, *buf_rnd;
U8 *in_rnd;
double signal = 0., noise = 0.;
int i;
PR_DBG( 4, "Not all data available yet\n" );
return 0;
}
buf_rnd = swr_malloc( size_in(0) * sizeof( SYMBOL_COMPLEX ) );
for ( i=0; i<size_in(0); i++ ){
switch( private->type ){
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++;
}
}
// Calculate signal energy
for ( i=0; i<size_in(0); i++ ){
signal += (double)( in[i].real ) * buf_rnd[i].real +
(double)( in[i].imag ) * buf_rnd[i].imag;
}
signal = signal / size_in(0);
noise += pow( (double)in[i].real - signal * buf_rnd[i].real, 2 ) +
pow( (double)in[i].imag - signal * buf_rnd[i].imag, 2 );
}
noise = noise / size_in(0) / 2;
(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 );
We have only 1 input, no output, 1 config-variable and 1 stats-variable, and lots of functions are not used:
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" );
In the makefile we have to tell the final name of the module, as well that we use the math-library:
MATH = true
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.
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.
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/Makefile.module
Makefile.module -> 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:
DEPENDS = random snr midamble rrc block
Let's have a look at the documentation:
random - snr_send - midamble - rrc - block -
matched_filter - snr_rcv
and additionally:
random - snr_rcv(2)
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 . 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.
test_chain = swr_chain_create(
NEW_SPC_VAR( ''random'', rnd ),
NEW_SPC_VAR( ''snr_send'' ),
NEW_SPC( ''midamble'' ),
NEW_SPC( ''rrc'' ),
NEW_SPC_VAR( ''block'' ),
NEW_SPC_VAR( ''matched_filter'', mafi ),
NEW_SPC_VAR( ''snr_rcv'', snr_rcv ),
END_CHAIN );
test_chain_2 = swr_chain_create(
NEW_SPC_VAR( ''random'', rnd2 ),
OLD_SPC_IN( snr_rcv, 1 ),
END_CHAIN );
swr_sdb_set_config_int( rnd2, "seed", 0x1234 );
swr_sdb_set_config_int( mid, "amplitude", 16384 / 4 );
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" ) );
}
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
OK, now that the module is written, a simple test-case shows that our module works (NOT), 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.
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:
Makefile
BS/Makefile
BS/radio_bs.c
MS/Makefile
MS/radio_ms.c
DEPENDS = rrc synch energy mapper midamble random \
rndstr spread sink cch macro_sch snr
DEPENDS = rrc synch energy mapper midamble random \
rndstr spread sink macro_synch macro_sch snr
This file also reflects the changes and has a very small documentation in it.
There is a lot of things to say about the basic system. You can find an introduction in . 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:
ch_up = swr_chain_create(
NEW_SPC_VAR( "random", rnd ),
NEW_SPC( "snr_send" ),
NEW_SPC( "midamble" ),
NEW_SPC( "rrc" ),
OLD_SPC_IN( stfa, 1 ),
CHAIN_END );
swr_stfa_notice_sdb( stfa, 1, rnd );
PR( "Ready to go" );
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:
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:
ch_rach = swr_chain_create(
OLD_SPC_OUT( stfa, 1 ),
NEW_SPC_VAR( "matched_filter", 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" ) );
}
swr_sdb_send_msg( rnd, SUBS_MSG_USER, NULL, -1 );
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.
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!
Linus Gasser 2004-04-14