\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 bytes snr_sendour module created above, takes some random bytes as input, and creates a QPSK output chest_sendinserts the training-sequence in the middle of the stream rrc Root Raised Cosine pulse-shape filtering blocka very simple channel-simulation rrc_rcv Applies again the Root Raised Cosine filtering chest_rcvuses the training-sequence to make a channel-estimation and does a matched-filtering on the received samples snr_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|)}