======Signal Processing====== =====CDB===== \index{CDB!Reference|(} 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==== \index{swr_spc_get_new_desc} \index{CDB!Reference!swr_spc_get_new_desc} 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==== \index{swr_spc_define_config_parameter} \index{CDB!Reference!swr_spc_define_config_parameter} \index{UM_CONFIG_*} 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==== \index{swr_spc_define_stats_parameter} \index{CDB!Reference!swr_spc_define_stats_parameter} \index{UM_STATS_*} 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: \index{PARAMETER_DEBUG} \index{PARAMETER_HIDE} * 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_| |int|INT|INT| |double|DOUBLE|DOUBLE| |complex double|DOUBLE_COMPLEX|DOUBLE_COMPLEX| |char[128]|STRING128|STRING128| |block_t|-|BLOCK| |image_t|-|IMAGE| |void *|POINTER|POINTER| |SYMBOL_COMPLEX|COMPLEX|COMPLEX| ====swr_spc_define_input==== \index{swr_spc_define_input} \index{CDB!Reference!swr_spc_define_input} \index{UM_INPUT} 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 \index{swr_spc_define_output} \index{CDB!Reference!swr_spc_define_output} \index{UM_OUTPUT} 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); \index{swr cdb register spc} \index{CDB!Reference!swr cdb register spc} swr_spc_id_t swr_cdb_register_spc( swr_spc_desc_t **desc, const char *name); ====Port Types==== \index{signal-types} These are the valid signal-types for the in- and output and their respective type-name: | |UM_(IN/OUT)PUT(SIG_| |U8|U8| |SYMBOL_S16|SYMBOL_S16| |int|S32| |double|DOUBLE| |complex double|DOUBLE_COMPLEX| |SYMBOL_COMPLEX|SYMBOL_COMPLEX| |SYMBOL_COMPLEX_S32|SYMBOL_COMPLEX_S32| |SYMBOL_MMX|SYMBOL_MMX| |SAMPLE_S12|SAMPLE_S12| \index{CDB!Reference|)} ====Port Flags==== The port-flags are described in subsub:port-flags-block-related 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===== \index{SDB!Reference|(} 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==== \index{SDB!Instantiation} \index{Instantiation} ===swr_chain_create=== \index{swr_chain_create} 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: \index{NEW_SPC*} \index{OLD_SPC*} * 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=== \index{swr_sdb_instantiate_name} 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=== \index{swr_connection_add} 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==== \index{SDB!stats-structure} \index{SDB!config-structure} 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. \index{swr_sdb_get_config_struct} \index{swr_sdb_get_stats_struct} 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. \index{swr_sdb_free_config_struct} \index{swr_sdb_free_stats_struct} 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: \index{swr_sdb_set_config_pointer} \index{swr_sdb_set_config_int} \index{swr_sdb_set_config_complex} \index{swr_sdb_set_config_double} \index{swr_sdb_set_config_symbol} 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: \index{swr_sdb_show_profile} 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. \index{SDB!Reference|)} =====Subsystem===== \index{Subsystem!Reference|(} As written in sub:spc_subsystem, 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 subsystems((Subsystem and Module are interchangeable in this context)) * 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==== \index{Messages} 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. \index{SUBS_MSG_*|see{Subsystem/Messages}} \index{Subsystem!Messages!SUBS_MSG_CONNECT} 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. \index{Subsystem!Messages!SUBS_MSG_DISCONNECT} 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. \index{Subsystem!Messages!SUBS_MSG_NEW_TRACK} \index{Subsystem!Messages!SUBS_MSG_NO_TRACK} 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. \index{Subsystem!Messages!SUBS_MSG_THREAD} 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_THREAD//((This is not the default setting, because threading of a module gives a sensible overhead)). 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. \index{Subsystem!Messages!SUBS_MSG_EXIT} 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=== \index{Subsystem!Messages!SUBS_MSG_PREPARE} 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. \index{Subsystem!Messages!SUBS_MSG_DATA} 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. \index{Subsystem!Messages!SUBS_MSG_PING} 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. \index{Subsystem!Messages!SUBS_MSG_USER} 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=== \index{Subsystem!Messages!SUBS_MSG_RECONFIG} 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. \index{Subsystem!Messages!SUBS_MSG_RESIZE} 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==== \index{Subsystem!Flags} 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=== \index{Subsystem!Flags!SUBS_STATUS_THREAD} \index{Subsystem!Flags!SUBS_STATUS_TRACKED} \index{Subsystem!Flags!SUBS_STATUS_RESIZE_DOWN} \index{Subsystem!Flags!SUBS_STATUS_RESIZE_UP} 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=== \index{Subsystem!Flags!SET_STATUS} \index{SET_STATUS} 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. \index{SUBS_STATUS_*|see{Subsystem/Flags}} \index{Subsystem!Flags!SUBS_STATUS_RESIZE_NONE} 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. \index{Subsystem!Flags!SUBS_STATUS_RESIZE_BOTH} 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. \index{Subsystem!Flags!SUBS_STATUS_PREPARE_SWALLOW} 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. \index{Subsystem!Flags!SUBS_STATUS_MULTI_IN} 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. \index{Subsystem!Flags!SUBS_STATUS_RECONF} \index{Subsystem!Flags!SUBS_STATUS_WORKING} \index{Subsystem!Flags!SUBS_STATUS_PREPARE} \index{Subsystem!Flags!SUBS_STATUS_LISTED} * 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 ===Block-related=== Besides the usual block- (port-)handling, some modules need a more special handling. These flags help define such special ports. \index{Subsystem!Ports!SUBS_PORT_OWN_MALLOC} \index{Subsystem!Ports!SUBS_PORT_OTHER_FREE} \index{Subsystem!Ports!SUBS_PORT_OTHER_MALLOC} \index{Subsystem!Ports!SUBS_PORT_THIS_FREE} \index{Subsystem!Ports!SUBS_PORT_PASSED_THROUGH} * 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=== \index{Subsystem!Ports!SUBS_PORT_DATA} 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. \index{Subsystem!Ports!SUBS_PORT_GOT_RESIZE} 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. \index{Subsystem!Reference|)} =====Module===== \index{Module|(} This section gives an overview of the module-creation and the use of it. Even though cha:conception_to_measurement 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: - Registration with the CDB, usually in //module_init//, this happens when loading the module into memory - Instantiation, which means setting up the needed memory and calling //init// - A call to //reconfig// to assure that everything is OK \index{swr_sdb_instantiate_name} \index{swr_sdb_instantiate_id} 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: \index{Module!pdata}pdataProcess an incoming data-block and eventually produce some output-data \index{Module!reconfig} reconfigReconfigure itself because one of the configuration variables have been changed \index{Module!resize} resizeRe-calculate its input- and output-sizes \index{Module!msg} custom-msgReact to a user-message \index{Module!finalize} finalizeClean up allocated values \index{swr_sdb_set_configure} 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: configWhere other modules may ask for a change in the behaviour statsResults from the signal-processing privateInternal 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 \index{swr_sdb_get_config_struct} swr_sdb_get_config_struct( context->id, (void**)&config ); before being able to use //config-\textgreater{//something}. To free the structure, use \index{swr_sdb_free_config_struct} 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** \index{Data Types!Block} 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** \index{Data Types!SYMBOL_COMPLEX} typedef struct { short int real; short int imag; } SYMBOL_COMPLEX; **SYMBOL_COMPLEX_S32** \index{Data Types!SYMBOL_COMPLEX_S32} typedef struct { int real; int imag; } SYMBOL_COMPLEX; **DOUBLE_COMPLEX** \index{Data Types!DOUBLE_COMPLEX} 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** \index{Data Types!SYMBOL_MMX} Describes one complex symbol in a special format. It is done like this: Re_{0} Im_{0} -Im_{0} Re_{0} 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: Re_{0} Im_{0} Re_{0} Im_{0} 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** \index{Data Types!U8}U8Unsigned 8-bit \index{Data Types!S8} S8Signed 8-bit \index{Data Types!U32} U32Unsigned 32-bit \index{Data Types!S32} S32Signed 32-bit \index{Data Types!SAMPLE_S12} SAMPLE_S12Signed 12-bit, where the 12 upper bits are used. For the available hardware, the lower 4 bits signal RX/TX \index{Data Types!SAMPLE_S16} SYMBOL_S16Signed 16-bit real symbol \index{Data Types!DOUBLE} 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: \index{Macros!UM_CONFIG_INT}UM_CONFIG_INTadds an int-parameter to the configuration \index{Macros!UM_CONFIG_COMPLEX} UM_CONFIG_COMPLEXadds a complex-parameter to the configuration \index{Macros!UM_CONFIG_DOUBLE} UM_CONFIG_DOUBLEadds a double-parameter to the configuration \index{Macros!UM_CONFIG_DOUBLE_COMPLEX} UM_CONFIG_DOUBLE_COMPLEXadds a double_complex-parameter to the configuration \index{Macros!UM_CONFIG_STR128} UM_CONFIG_STR128adds a char[128] parameter to the configuration \index{Macros!UM_CONFIG_POINTER} UM_CONFIG_POINTERadds a void* parameter to the configuration \index{Macros!UM_STATS_INT} UM_STATS_INTadds an int-parameter to the statistics \index{Macros!UM_STATS_COMPLEX} UM_STATS_COMPLEXadds a complex parameter to the stats \index{Macros!UM_STATS_DOUBLE} UM_STATS_DOUBLEadds a double-parameter to the statistics \index{Macros!UM_STATS_DOUBLE_COMPLEX} UM_STATS_DOUBLE_COMPLEXadds a double_complex-parameter to the statistics \index{Macros!UM_STATS_STR128} UM_STATS_STR128adds a char[128] parameter to the statistics \index{Macros!UM_STATS_POINTER} UM_STATS_POINTERadds a void* parameter to the statistics \index{Macros!UM_STATS_BLOCK} UM_STATS_BLOCKadds a block_t parameter to the statistics, see par:Blocks \index{Macros!UM_STATS_IMAGE} UM_STATS_IMAGEadds an image to the stats \index{Macros!UM_INPUT} UM_INPUTadds an input-port, for the types see sub:Data-types, and allows to define a flag \index{Macros!UM_OUTPUT} UM_OUTPUTadds an output-port, for the types see sub:Data-types, and allows to define a flag ===other functions=== \index{Macros!private}privateallows access to this modules private-structure \index{Macros!size_in} 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. \index{Macros!size_out} size_out(n)returns the output-size of the port //n//. Allocating sizes is possible as with //size_in//. \index{Macros!data_available} data_available(n)returns true if the input-port //n// has some new data \index{Macros!buffer_in} buffer_in(n)returns a pointer to the input-buffer //n// and clears the data-flag on this input-port \index{Macros!buffer_out} buffer_out(n)returns a pointer to the output-buffer //n// and sets the data-flag on this output-port \index{Macros!call_module} call_modulesends a MSG_DATA to the module \index{Macros!make_thread} make_threadputs a module in a thread \index{Module|)}