Signal Processing

CDB

<texit>\index{CDB!Reference|(}</texit> There are two classes of functions in the CDB: defining a module and requesting informations about defined modules. While the latter is only used internally of the software-radio, the former is also used in the module-definition. Both type of functions are also described briefly in Include/cdb.h.

The Class Data Base has a reference to all announced modules in the system. Only a module that is written in here can be instantiated, get into the SDB and be connected to other modules. These are the functions used to add a new module to the CDB. Every function has also a counterpart-macro that is defined in Include/spc.h.

swr_spc_get_new_desc

<texit>\index{swr_spc_get_new_desc}</texit> <texit>\index{CDB!Reference!swr_spc_get_new_desc}</texit>

First, it has to ask for a new descriptor, using the following function:

swr_spc_desc_t *swr_spc_get_new_desc(
int nbr_inputs,        // The maximum number of inputs
int nbr_outputs,       // The maximum number of outputs
int nbr_config_params, // How many configuration parameters
int nbr_stats_params); // How many statistic parameters

This function allocates the necessary memory to store the required parameters and makes sure there is a place in the internal database. All required in- and outputs need to be defined, as well as all config- and stats-parameters. This can be done using the functions that are described hereafter. Each of this function is also used in a macro defined in Include/spc.h for easier reference.

swr_spc_define_config_parameter

<texit>\index{swr_spc_define_config_parameter}</texit> <texit>\index{CDB!Reference!swr_spc_define_config_parameter}</texit> <texit>\index{UM_CONFIG_*}</texit>

Every configuration-parameter has to be defined in the order of appearance in the config_t-structure. The types have to be the same. Instead of using the following function, you can resort to the UM_CONFIG_*-functions. So, to define an INTeger, you can use UM_CONFIG_INT(“Name”);.

int swr_spc_define_config_parameter(
  swr_spc_desc_t *cdb_desc,  // The description received
  parameter_type_t type,     // The type of the variable
  unsigned long flags,       // Eventual flags
  const char *name);         // The name to use in the software-radio

If you have the following configuration-structure:

typedef struct{
  int slots;
  double amplitude;
} config_t;

then you need to define the two configuration-parameters with the following macros:

UM_CONFIG_INT( "slots" );
UM_CONFIG_DOUBLE( "amp" );

Note that the order and the type need to correspond, but not the name!

swr_spc_define_stats_parameter

<texit>\index{swr_spc_define_stats_parameter}</texit> <texit>\index{CDB!Reference!swr_spc_define_stats_parameter}</texit> <texit>\index{UM_STATS_*}</texit>

Correspondingly to defining the config-parameters, all stats-parameters need to be defined, too. The same restrictions with regard to order and type apply. There are also macros that call this function. They are called UM_STATS_*, where the types are the same as for the config-parameters.

int swr_spc_define_stats_parameter(
  swr_spc_desc_t *desc,  // The description-handler
  parameter_type_t type, // The type of the stats
  unsigned long flags,   // Eventual flags
  const char *name);     // Name visible in the software-radio

If you have the following stats-structure:

typedef {
  complex double channel;
  double SNR;
} stats_t;

Then you have to define them with the following macros:

UM_STATS_DOUBLE_COMPLEX( "H" );
UM_STATS_DOUBLE( "SNR" );

Note again how the order and the types need to correspond, but not the names!

Flags for define_*_parameter

There are two flags for the config- and stats-parameters that can be defined when defining these parameters: <texit>\index{PARAMETER_DEBUG}</texit> <texit>\index{PARAMETER_HIDE}</texit>

  • PARAMETER_DEBUG shows a parameter only when debug-mode is active in visualize
  • PARAMETER_HIDE never shows a parameter in the visualize-tool

Types for define_*_parameter

These are the valid types for the stats- and the config-parameters:

UM_CONFIG_UM_STATS_
intINTINT
doubleDOUBLEDOUBLE
complex doubleDOUBLE_COMPLEXDOUBLE_COMPLEX
char[128]STRING128STRING128
block_t-BLOCK
image_t-IMAGE
void *POINTERPOINTER
SYMBOL_COMPLEXCOMPLEXCOMPLEX

swr_spc_define_input

<texit>\index{swr_spc_define_input}</texit> <texit>\index{CDB!Reference!swr_spc_define_input}</texit> <texit>\index{UM_INPUT}</texit>

All inputs to the module need to be defined using this function. Again, a macro exists that englobes this function. Contrary to the config- and stats- definitions, there is only one macro, and you have to give it a paramater for the type. Even if you intend to use only a subset of the inputs at any given time, you have to define all of them.

int swr_spc_define_input(
  swr_spc_desc_t *desc,          // A pointer to the description
  swr_signal_type_t signal_type, // The signal type
  unsigned long flags);          // Eventual flags

<texit>\index{swr_spc_define_output}</texit> <texit>\index{CDB!Reference!swr_spc_define_output}</texit> <texit>\index{UM_OUTPUT}</texit>

All outputs to the module need to be defined using this function. Again, a macro exists that englobes this function. Contrary to the config- and stats- definitions, there is only one macro, and you have to give it a paramater for the type. Even if you intend to use only a subset of the outputs at any given time, you have to define all of them.

int swr_spc_define_output(
  swr_spc_desc_t *desc,
  swr_signal_type_t signal_type,
  unsigned long flags);

<texit>\index{swr cdb register spc}</texit> <texit>\index{CDB!Reference!swr cdb register spc}</texit>

swr_spc_id_t swr_cdb_register_spc(
  swr_spc_desc_t **desc,
  const char *name);

Port Types

<texit>\index{signal-types}</texit>

These are the valid signal-types for the in- and output and their respective type-name:

UM_(IN/OUT)PUT(SIG_
U8U8
SYMBOL_S16SYMBOL_S16
intS32
doubleDOUBLE
complex doubleDOUBLE_COMPLEX
SYMBOL_COMPLEXSYMBOL_COMPLEX
SYMBOL_COMPLEX_S32SYMBOL_COMPLEX_S32
SYMBOL_MMXSYMBOL_MMX
SAMPLE_S12SAMPLE_S12

<texit>\index{CDB!Reference|)}</texit>

Port Flags

The port-flags are described in <ref>subsub:port-flags-block-related</ref> and can either be directly added when creating the ports, or in the _init-block of the module by a line like

  port_in(0).flags = SWR_PORT_OWN_MALLOC;

SDB

<texit>\index{SDB!Reference|(}</texit> Once a module is defined in the CDB, it can be instantiated. When this is done, the SDB allocates private space for each instantiation and makes sure that each time an instance is called, it has the reference to its own private space.

Furthermore the SDB offers functions to have access to the config- and stats-structures from both the inside and the outside of a module. The difference is, that inside of the module you have an exact knowledge of the structure to change, while outside of the module you don't know anything else than the type and the name of the parameter to change or read.

Then the SDB offers some more special functions to access internal structures like the port in- and outputs and the profiling.

Instantiation

<texit>\index{SDB!Instantiation}</texit> <texit>\index{Instantiation}</texit>

swr_chain_create

<texit>\index{swr_chain_create}</texit>

Usually you will instantiate a chain of modules, using the function swr_chain_create. It takes a number of arguments to modules that should form a chain. The following arguments are valid: <texit>\index{NEW_SPC*}</texit> <texit>\index{OLD_SPC*}</texit>

  • NEW_SPC( “name” ) Instantiates the module “name” and connects its input 0 to the previous output (if any) and its output 0 to the next input (if any)
  • NEW_SPC_IN( “name”, in ) as NEW_SPC, but the input port 'in' is connected to the output-port of the previous module
  • NEW_SPC_OUT( “name”, out ) as NEW_SPC, but the output port 'out' is connected to the input-port of the next module
  • NEW_SPC_IN_OUT( “name”, in, out ) as NEW_SPC, but the input-port 'in' is connected to the previous module, and the output-port 'out' is connected to the next module
  • NEW_SPC_VAR( “name”, var ) same as NEW_SPC, but the id of the instantiated module is stored in 'var'
  • NEW_SPC_VAR_IN( “name”, var, in ) same as NEW_SPC_IN, but the id of the instantiated module is stored in 'var'
  • NEW_SPC_VAR_OUT( “name”, var, out ) same as NEW_SPC_OUT, but the id of the instantiated module is stored in 'var'
  • NEW_SPC_VAR_IN_OUT( “name”, var, in, out ) same as NEW_SPC_IN_OUT, but the id of the instantiated module is stored in 'var'
  • OLD_SPC( var ) same as NEW_SPC, but instead of instantiating a new module, takes an alread instantiated module 'var'
  • OLD_SPC_IN( var ) same as NEW_SPC_IN, but instead of instantiating a new module, takes an alread instantiated module 'var'
  • OLD_SPC_OUT( var ) same as NEW_SPC_OUT, but instead of instantiating a new module, takes an alread instantiated module 'var'
  • OLD_SPC_IN_OUT( var ) same as NEW_SPC_IN_OUT, but instead of instantiating a new module, takes an already instantiated module 'var'
  • CHAIN_END indicates that the chain is finished.

The function returns an identifier to the created chain, so that the whole chain can be deleted when not in use anymore. In order to function correctly, at least two modules must be given as arguments.

swr_sdb_instantiate_name

<texit>\index{swr_sdb_instantiate_name}</texit> If you only want to create one instance, for example for a module that will be connected to multiple other modules, or for a module that doesn't have any in- or ouputs, you can use swr_sdb_instantiate_name. It takes as argument the name of the module and returns a sdb-id or -1 if an error occurs.

swr_connection_add

<texit>\index{swr_connection_add}</texit>

Once you created a chain or some single modules, you might want to create connections 'by hand'. Then you need the following function:

swr_conn swr_conn_add(
  swr_sdb_id sender,   // The id of the sending module
  int output,          // The output-port of the sending module
  swr_sdb_id receiver, // The id of the receiving module
  int input );         // The input-port of the receiving module

Manipulating stats- and config-structures

<texit>\index{SDB!stats-structure}</texit> <texit>\index{SDB!config-structure}</texit>

There are two possibilities: either a module wants to change its own structures, or a part of the software-radio outside of the module wants to read or write one of the structures. This can be another module, or the visualize-tool. Due to the different sources that may be using the config- and stats-structures, they have to be mutex'ed, so that the information read is always up-to-date and doesn't change in a critical way.

Accessing own Structures

For the modules own structures, a pair of functions exist to request the mutex to either the config- or the stats-structure. Once the mutex is aquired, the module can access it as it likes, before giving back the mutex. In order to force the user not to access the structures outside of a mutexed environment, a pointer to the structures is passed and is initialised with the address of the structure, or with NULL when the mutex is released. In this way the software-radio will immediatly show a bug when the structures are accessed outside of a mutexed environment. <texit>\index{swr_sdb_get_config_struct}</texit> <texit>\index{swr_sdb_get_stats_struct}</texit>

In order to aquire the mutex, one of the two functions has to be called:

int swr_sdb_get_config_struct( swr_sdb_id id, void **str );
int swr_sdb_get_stats_struct( swr_sdb_id id, void **str );

As this is usually done in the module, the context-variable is available, so it is used like this:

config_t *config;
swr_sdb_get_config_struct( context->id, &config );

Now the module can read and write to the configuration-structure. The stats-structure is used in the same way. <texit>\index{swr_sdb_free_config_struct}</texit> <texit>\index{swr_sdb_free_stats_struct}</texit>

To release the mutex, also two functions exist:

int swr_sdb_free_config_struct( swr_sdb_id id, void **str );
int swr_sdb_free_stats_struct( swr_sdb_id id, void **str );

As described above, they take a pointer to a pointer of the struct. In this way, using the pointer incorrectly causes a segmentation-fault (or a kernel-Oops in real-time).

Accessing other Structures

When accessing structures from other modules, one not only has to take care about mutual exclusion, but also one has to notify the module that something changed, at least in the case of a configuration-change.

To change the configuration of another module, one has to know the id of that module, the name of the configuration-variable and the type. Then one can set the value using one of the swr_sdb_set_config_*-functions: <texit>\index{swr_sdb_set_config_pointer}</texit> <texit>\index{swr_sdb_set_config_int}</texit> <texit>\index{swr_sdb_set_config_complex}</texit> <texit>\index{swr_sdb_set_config_double}</texit> <texit>\index{swr_sdb_set_config_symbol}</texit>

int swr_sdb_set_config_pointer(  swr_sdb_id id, char *name, void *value );
int swr_sdb_set_config_int(  swr_sdb_id id, char *name, int value );
int swr_sdb_set_config_complex(  swr_sdb_id id, char *name, complex double value );
int swr_sdb_set_config_double(  swr_sdb_id id, char *name, double value );
int swr_sdb_set_config_symbol(  swr_sdb_id id, char *name, SYMBOL_COMPLEX value );

There is a special case where one wants to wait for the reconfiguration of the module, for example when changing a range of configurations from the same module. For this reason, the 'id'-parameter of the above functions can be chosen to be negative. Internally, all sdb-ids are positive, so a negative id always points to a unique module and stands for: “don't reconfigure right now”. The other case is when reading stats-structures of other modules. The function-names are:

void* swr_sdb_get_stats_pointer( ... );
int swr_sdb_get_stats_int( ... );
complex double swr_sdb_get_stats_complex( ... );
double swr_sdb_get_stats_double( ... );
SYMBOL_COMPLEX swr_sdb_get_stats_symbol( ... );
block_t swr_sdb_get_stats_block( ... );
image_t swr_sdb_get_stats_image( ... );

All functions have \texttt{swr_sdb_id id, char *name} as parameter. Here a negative sdb-id as 'id'-parameter is invalid.

Other Functions

Most other functions from the SDB are only used internally and are documented in Include/sdb.h. The only exception is the function to read and display the profile of a module: <texit>\index{swr_sdb_show_profile}</texit>

int swr_sdb_show_profile( swr_sdb_id id );

which displays all available profiles of the given module complete with number of calls, total time and average time. <texit>\index{SDB!Reference|)}</texit>

Subsystem

<texit>\index{Subsystem!Reference|(}</texit>

As written in <ref>sub:spc_subsystem</ref>, the subsystem is the base-class for all modules. As such it is responsible for correct message-passing and cleaning up of the modules. Furthermore it keeps track and acts upon different flags that may be set in the subsytem and the ports. So there are three places that describe more or less entierly the state of the subsystem:

  • Messages which are passed between subsystems1)
  • Subsystem-flags reflecting the state of the subsystem
  • Port-flags showing the state of each port individually

In the following three subsections you'll find a description of each of these systems.

Messages

<texit>\index{Messages}</texit>

Each message that is passed to a subsystem has three arguments: message-id, data and return-id. The message-id tells the subsystem what it needs to do. The data-part is a (void*)-pointer, and should be set to NULL when it's not used. The return-id is used when a return-message could be generated, and should contain the sender-id. If the sender has no id (is not a module), the sender-id should be set to -1.

The messages defined in the message-id can be divided in three groups:

  • Basic handling involves everything to set-up the module and is rarely or never called during life-time
  • Reconfiguration of the module is also pretty rare for most of the modules
  • Data Propagation is the workhorse of the subsystem and modules

Each group is described in more detail in the following sections.

Basic Handling

After the initialisation of a subsystem, everything is ready to connect this subsystem to another subsystem. <texit>\index{SUBS_MSG_*|see{Subsystem/Messages}}</texit> <texit>\index{Subsystem!Messages!SUBS_MSG_CONNECT}</texit>

Connecting is done by sending the message SUBS_MSG_CONNECT to both subsystems that are to be connected together. As payload for the message one should give a structure of type swr_propagation_t. This structure contains all necessary information: port-#, size, flags, block-address, sdb-id of the other end and the direction. If one of the ports is already defined with regard to its size, it will communicate this to the connect-function, which will inform the other port of the desired size.

<texit>\index{Subsystem!Messages!SUBS_MSG_DISCONNECT}</texit> The SUBS_MSG_DISCONNECT message works in a similar way. One has to take care that both messages don't inform the other subsystem of the change. A function wanting to connect or disconnect two modules has to inform both of the action to take.

<texit>\index{Subsystem!Messages!SUBS_MSG_NEW_TRACK}</texit> <texit>\index{Subsystem!Messages!SUBS_MSG_NO_TRACK}</texit> The user can ask for tracking of certain values. Whenever a subsystem is asked to track its values, it is sent a SUBS_MSG_NEW_TRACK message, after which the subsystem will check the tracking-list on each data-processing to update the corresponding tracks. Similarly, SUBS_MSG_NO_TRACK is sent to tell the subsytem to stop searching the tracking-list. This pair of messages exists because tracking is quite rare and asks for some processing-power in order to update all necessary lists. So, as long as the subsystem didn't receive a SUBS_MSG_NEW_TRACK-message, it won't search through the list.

<texit>\index{Subsystem!Messages!SUBS_MSG_THREAD}</texit> Even though the software-radio is conceived as a real-time radio, some modules take more time because of their complexity. In order to assure that the rest of the software-radio is not affected by a complex module, it is possible to put the module in a thread by sending it a SUBS_MSG_THREAD2). When receiving this message, the subsystem sets up a thread and will activate this thread whenever it receives a SUBS_MSG_DATA message. For all other messages, the subsystem will run in the context of the calling function.

<texit>\index{Subsystem!Messages!SUBS_MSG_EXIT}</texit> Finally, a subsystem will stop working upon receiving a SUBS_MSG_EXIT-message. All input- and output-ports have to be cleaned up before sending this messasge, otherwise undefined behaviour might occur.

Data Propagation

<texit>\index{Subsystem!Messages!SUBS_MSG_PREPARE}</texit>

In a multi-threaded real-time environment one has to take care that things don't get mixed up. For this reason, before asking a module to do some calculations on data, one has to send it a SUBS_MSG_PREPARE-message. This message is propagated to all connected outputs where it is further propagated. If any of the connected modules is still working, the message returns a SUBS_STATUS_WORKING, and the caller should wait for a later time.

<texit>\index{Subsystem!Messages!SUBS_MSG_DATA}</texit> If the prepare-message returned 0 (for OK), that means that all modules in the chain are prepared and can be called by sending a SUBS_MSG_DATA-message to the top module. This message will test for SUBS_STATUS_MULTI_IN and SUBS_STATUS_THREAD and react accordingly. If appropriate, it will call the pdata-function of the module. Upon returning, the output-ports are checked for new data, and the modules connected to output-ports containing new data are sent a SUBS_MSG_DATA-message.

<texit>\index{Subsystem!Messages!SUBS_MSG_PING}</texit> A small test-message that survived from the depths of the development is the SUBS_MSG_PING-message, which has no direct effect on the subsystem.

<texit>\index{Subsystem!Messages!SUBS_MSG_USER}</texit> In order to allow for user-defined messages to the modules, the SUBS_MSG_USER-message exists. The payload of the message can contain whatever is accurate. Upon reception of this message, the user_msg-function of the module is called, with the payload as argument.

Reconfiguration

<texit>\index{Subsystem!Messages!SUBS_MSG_RECONFIG}</texit>

Whenever a part of the software-radio thinks that the configuration might have changed, it sends a SUBS_MSG_RECONFIG-message to the corresponding module. If the receiving module has the flag SUBS_STATUS_RECONFIG set, it will call the reconfig-function of the module. Furthermore the configure_inputs or configure_outputs-function is called, depending on whether the SUBS_STATUS_RESIZE_UP or -DOWN flag is set.

<texit>\index{Subsystem!Messages!SUBS_MSG_RESIZE}</texit> Upon arrival of a message, the subsystem stores all input- and output-port addresses, as well as the sizes. If something changes during the execution of the message, a SUBS_MSG_RESIZE-message is sent to all ports that changed size or the data-pointer.

Subsytem-Flags

<texit>\index{Subsystem!Flags}</texit> These flags reflect the internal state of the susbsystem and are split in these groups:

  • Propriety reflect a general state of this subsystem which is more or less static
  • User-defined, that is, set in the _init-part of the module
  • State for transient information about the module

Propriety

<texit>\index{Subsystem!Flags!SUBS_STATUS_THREAD}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_TRACKED}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_RESIZE_DOWN}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_RESIZE_UP}</texit>

All these flags are set internally by the software-radio and change very rarely.

  • SUBS_STATUS_THREAD module has been threaded
  • SUBS_STATUS_TRACKED there is a stats_track list with this module
  • SUBS_STATUS_RESIZE_DOWN resize-messages go down
  • SUBS_STATUS_RESIZE_UP resize-messages go up

The RESIZE-flags are set the first time a module receives a resize-message. This is done to know in the future which port-sizes have precedence, because in some situations it's not straightforward to decide what to do if there is not a clear preference for a certain resize-direction.

User-defined

<texit>\index{Subsystem!Flags!SET_STATUS}</texit> <texit>\index{SET_STATUS}</texit>

All these flags can be set in the _init-part of the module by inserting a line

SET_STATUS( RESIZE_NONE );

One has to note that with the SET_STATUS-command the SUBS_STATUS_-part of the flag has to be omitted.

<texit>\index{SUBS_STATUS_*|see{Subsystem/Flags}}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_RESIZE_NONE}</texit> A module like the STFA only generates resize-requests, and will never receive one. The usual logic of the subsystem forbids this, but if you set the SUBS_STATUS_RESIZE_NONE-flag the subsystem will honor this behaviour.

<texit>\index{Subsystem!Flags!SUBS_STATUS_RESIZE_BOTH}</texit> While some modules don't want to receive resize-requests, other modules like the test_data_rcv need to be informed by changes on both the input and the output. If this is the case, you have to set the SUBS_STATUS_RESIZE_BOTH-flag. Afterwards the module will be alerted by any size-change on its input- and output-ports, and the subsystem won't complain about this strange behaviour.

<texit>\index{Subsystem!Flags!SUBS_STATUS_PREPARE_SWALLOW}</texit> The SUBS_MSG_PREPARE-message traverses all attached modules. Of course it has to stop at the STFA, else every module will be in prepare-status. If a module has the SUBS_STATUS_PREPARE_SWALLOW-flag set, then it will silently drop all requests to prepare and it will not inform other modules attached to itself.

<texit>\index{Subsystem!Flags!SUBS_STATUS_MULTI_IN}</texit> If you have a module with multiple intputs, and you want to make sure that all connected inputs contain up-to-date data, you can set the SUBS_STATUS_MULTI_IN-flag. This will tell the subsystem to make sure that all inputs contain data before calling the pdata-method of the module.

An important issue when using the MULTI_IN-flag is the fact that the subsystem will try to make sure that all inputs are from the same time-instant. For this reason, the inputs of the module that has the MULTI_IN-flag set need to arrive in chronological order. Taking the example of a MIMO_LDPC-decoder, the first input has to come from the first STFA, the second input from the second STFA and so on. This is the only way that the subsytem can make sure that all inputs come from the same frame.

State

The states described here are very short-lived. They usually indicate a work in progress or a needed action.

<texit>\index{Subsystem!Flags!SUBS_STATUS_RECONF}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_WORKING}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_PREPARE}</texit> <texit>\index{Subsystem!Flags!SUBS_STATUS_LISTED}</texit>

  • SUBS_STATUS_RECONF is set when the configuration-parameters have been changed, but before the module's reconfig-method has been called.
  • SUBS_STATUS_WORKING indicates a module that is in it's pdata-method
  • SUBS_STATUS_PREPARE is a module that is 'locked' and ready to process data.
  • SUBS_STATUS_LISTED in conjunction with the debug-interface, indicates a module that is known to the visualize-tool

Port-Flags

These flags are individual for each input- and output-port. They can be combined together, although not all combinations make sense. There are mainly two groups of port-flags:

  • Block-related which define how the block is allocated and who takes care about malloc/free
  • Data-passing which describe when a block of data is ready or when it needs to change

Besides the usual block- (port-)handling, some modules need a more special handling. These flags help define such special ports. <texit>\index{Subsystem!Ports!SUBS_PORT_OWN_MALLOC}</texit> <texit>\index{Subsystem!Ports!SUBS_PORT_OTHER_FREE}</texit> <texit>\index{Subsystem!Ports!SUBS_PORT_OTHER_MALLOC}</texit> <texit>\index{Subsystem!Ports!SUBS_PORT_THIS_FREE}</texit> <texit>\index{Subsystem!Ports!SUBS_PORT_PASSED_THROUGH}</texit>

  • SWR_PORT_OWN_MALLOC this means that the module wants to keep track on it's own about the different malloc/free
  • SWR_PORT_OTHER_FREE another port is responsible for freeing this data
  • SWR_PORT_OTHER_MALLOC another port is allocating the memory
  • SWR_PORT_THIS_FREE this port is responsible for freeing the data
  • SWR_PORT_PASSED_THROUGH this port passes the data through

Signal-passing

<texit>\index{Subsystem!Ports!SUBS_PORT_DATA}</texit> The flag SUBS_PORT_DATA is set whenever a module requests a buffer by using buffer_out(port). When terminating the subsystem-call, it checks for this flag on all output-ports and makes sure that the appropriate attached modules are called.

<texit>\index{Subsystem!Ports!SUBS_PORT_GOT_RESIZE}</texit> The SUBS_PORT_GOT_RESIZE-flag is only used internally to mark a port that already has been resized. Without it, one could have a ping-pong of two ports that try to resize each other mutually. <texit>\index{Subsystem!Reference|)}</texit>

Module

<texit>\index{Module|(}</texit>

This section gives an overview of the module-creation and the use of it. Even though <ref>cha:conception_to_measurement</ref> gives an example of how to create a new module, it is a good idea to read at least this introduction, so that you know what it is about.

General introduction

Before a module can be used, it usually has to go through the following steps:

  1. Registration with the CDB, usually in module_init, this happens when loading the module into memory
  2. Instantiation, which means setting up the needed memory and calling init
  3. A call to reconfig to assure that everything is OK

<texit>\index{swr_sdb_instantiate_name}</texit> <texit>\index{swr_sdb_instantiate_id}</texit> The points 2 and 3 are done automatically when calling swr_sdb_instantiate_* and may happen more than once, where a new memory-block is allocated for each instantiation, in order to make sure that all copies of the module work in an independent way.

Once this has been done, a module can be asked to do one of the following tasks:

<texit>\index{Module!pdata}</texit>
pdata
Process an incoming data-block and eventually produce some output-data <texit>\index{Module!reconfig}</texit>
reconfig
Reconfigure itself because one of the configuration variables have been changed <texit>\index{Module!resize}</texit>
resize
Re-calculate its input- and output-sizes <texit>\index{Module!msg}</texit>
custom-msg
React to a user-message <texit>\index{Module!finalize}</texit>
finalize
Clean up allocated values

<texit>\index{swr_sdb_set_configure}</texit> The names to the left are the internal names used in the module-definition. You will never call these functions directly, but rather ask the MSR to do something that will then call one of these functions. So if you reconfigure one module using swr_sdb_set_configure_int you ask the MSR to set the configuration of this module-instance to a certain value and to call the appropriate reconfig function.

Data Structures

A module has three different data-structures:

config
Where other modules may ask for a change in the behaviour
stats
Results from the signal-processing
private
Internal structure that is not available to the outside

While the first two have already been discussed a bit, the third is new. It may be used for internal tables built depending on the configuration, it may contain a copy of important config-parameters or anything else needed for a module to function correctly. An important point: the private-structure is personal to each copy of the module, so it is not suited to keep 'global' options.

The config and stats structures are protected by mutexes, as they are open to all other modules to use. So in order to use a config-structure, one has first to call

<texit>\index{swr_sdb_get_config_struct}</texit>

swr_sdb_get_config_struct( context->id, (void**)&config );

before being able to use config-\textgreater{something}. To free the structure, use

<texit>\index{swr_sdb_free_config_struct}</texit>

swr_sdb_free_config_struct( context->id, (void**)&config );

after which other modules can alos access this structure. The same goes for the stats-structure. You don't have to make this extra effort with the private-structure, as they are local to each instance anyway.

Data Types

For Config and Stats

Blocks

<texit>\index{Data Types!Block}</texit> Blocks are a defined in the following way:

typedef struct {
  void *data;
  int size;
  swr_signal_type_t type;
} block_t;

They can be used to give a window into an internal vector. The matched-filter module for example has a block that points to the matched-filter used, so the user can see the matched-filter in real-time, using the visualisation tool.

The data pointer has to point to the vectory you want to display, size is the size in units of type, which is one of the Data-Types described in here (w/o Block, of course).

SYMBOL_COMPLEX <texit>\index{Data Types!SYMBOL_COMPLEX}</texit>

typedef struct {
  short int real;
  short int imag;
} SYMBOL_COMPLEX;

SYMBOL_COMPLEX_S32 <texit>\index{Data Types!SYMBOL_COMPLEX_S32}</texit>

typedef struct {
  int real;
  int imag;
} SYMBOL_COMPLEX;

DOUBLE_COMPLEX <texit>\index{Data Types!DOUBLE_COMPLEX}</texit>

typedef struct {
  double real;
  double imag;
} double_complex;

This structure is compatible with the complex double declaration from C. So, if you include “complex.h”, you can declare a complex double and tell the subsystem to use it as such.

SYMBOL_MMX <texit>\index{Data Types!SYMBOL_MMX}</texit>

Describes one complex symbol in a special format. It is done like this:

Graph

The utility of this is that if we want to do a complex multiplication, we can arrange the second complex number in the following way:

Graph

And then a special MMX-operation on these two complex numbers yields directly the result, separated into real and imaginary part. This is very useful for convolutions that need to be optimised.

Simple Data-types

<texit>\index{Data Types!U8}</texit>
U8
Unsigned 8-bit <texit>\index{Data Types!S8}</texit>
S8
Signed 8-bit <texit>\index{Data Types!U32}</texit>
U32
Unsigned 32-bit <texit>\index{Data Types!S32}</texit>
S32
Signed 32-bit <texit>\index{Data Types!SAMPLE_S12}</texit>
SAMPLE_S12
Signed 12-bit, where the 12 upper bits are used. For the available hardware, the lower 4 bits signal RX/TX <texit>\index{Data Types!SAMPLE_S16}</texit>
SYMBOL_S16
Signed 16-bit real symbol <texit>\index{Data Types!DOUBLE}</texit>
DOUBLE
a double floating-point value

Macros

Each function that is defined in a module takes at least one argument: swr_sdb_t *context In there all necessary information to distinguish one instance of another is stored. As this information may be a bit difficult to access, a lot of macros allow easy access to this information. These macros are defined in spc.h which is already included in the templates.

module_init

This function is a bit special in that it only registers the module with the CDB and doesn't do any actual signal-processing. So these are the macros you can use:

<texit>\index{Macros!UM_CONFIG_INT}</texit>
UM_CONFIG_INT
adds an int-parameter to the configuration <texit>\index{Macros!UM_CONFIG_COMPLEX}</texit>
UM_CONFIG_COMPLEX
adds a complex-parameter to the configuration <texit>\index{Macros!UM_CONFIG_DOUBLE}</texit>
UM_CONFIG_DOUBLE
adds a double-parameter to the configuration <texit>\index{Macros!UM_CONFIG_DOUBLE_COMPLEX}</texit>
UM_CONFIG_DOUBLE_COMPLEX
adds a double_complex-parameter to the configuration <texit>\index{Macros!UM_CONFIG_STR128}</texit>
UM_CONFIG_STR128
adds a char[128] parameter to the configuration <texit>\index{Macros!UM_CONFIG_POINTER}</texit>
UM_CONFIG_POINTER
adds a void* parameter to the configuration <texit>\index{Macros!UM_STATS_INT}</texit>
UM_STATS_INT
adds an int-parameter to the statistics <texit>\index{Macros!UM_STATS_COMPLEX}</texit>
UM_STATS_COMPLEX
adds a complex parameter to the stats <texit>\index{Macros!UM_STATS_DOUBLE}</texit>
UM_STATS_DOUBLE
adds a double-parameter to the statistics <texit>\index{Macros!UM_STATS_DOUBLE_COMPLEX}</texit>
UM_STATS_DOUBLE_COMPLEX
adds a double_complex-parameter to the statistics <texit>\index{Macros!UM_STATS_STR128}</texit>
UM_STATS_STR128
adds a char[128] parameter to the statistics <texit>\index{Macros!UM_STATS_POINTER}</texit>
UM_STATS_POINTER
adds a void* parameter to the statistics <texit>\index{Macros!UM_STATS_BLOCK}</texit>
UM_STATS_BLOCK
adds a block_t parameter to the statistics, see <ref>par:Blocks</ref> <texit>\index{Macros!UM_STATS_IMAGE}</texit>
UM_STATS_IMAGE
adds an image to the stats <texit>\index{Macros!UM_INPUT}</texit>
UM_INPUT
adds an input-port, for the types see <ref>sub:Data-types</ref>, and allows to define a flag <texit>\index{Macros!UM_OUTPUT}</texit>
UM_OUTPUT
adds an output-port, for the types see <ref>sub:Data-types</ref>, and allows to define a flag

other functions

<texit>\index{Macros!private}</texit>
private
allows access to this modules private-structure <texit>\index{Macros!size_in}</texit>
size_in(n)
returns the input-size of the port n. This may also be used to assign a size to a port, so size_in(0)=256; is valid. <texit>\index{Macros!size_out}</texit>
size_out(n)
returns the output-size of the port n. Allocating sizes is possible as with size_in. <texit>\index{Macros!data_available}</texit>
data_available(n)
returns true if the input-port n has some new data <texit>\index{Macros!buffer_in}</texit>
buffer_in(n)
returns a pointer to the input-buffer n and clears the data-flag on this input-port <texit>\index{Macros!buffer_out}</texit>
buffer_out(n)
returns a pointer to the output-buffer n and sets the data-flag on this output-port <texit>\index{Macros!call_module}</texit>
call_module
sends a MSG_DATA to the module <texit>\index{Macros!make_thread}</texit>
make_thread
puts a module in a thread
<texit>\index{Module|)}</texit>

1) Subsystem and Module are interchangeable in this context
2) This is not the default setting, because threading of a module gives a sensible overhead

Last modified:: %2007/%02/%21 %16:%Feb