diff options
author | Marc Lichtman <marcll@vt.edu> | 2018-11-02 00:53:52 -0400 |
---|---|---|
committer | Marc L <marcll@vt.edu> | 2019-06-07 12:45:14 -0400 |
commit | 73b074f121b0ab2ac38336916a60891c4d88d2cb (patch) | |
tree | de8f1782c897af3c278c224dcfbf96345c5c6f1d /docs/doxygen | |
parent | b6f15c59e96aa83142c47aeacd64da793dd8ba31 (diff) |
docs: moved usage manual to wiki
docs: first snapshot of wiki's usage manual
Diffstat (limited to 'docs/doxygen')
-rw-r--r-- | docs/doxygen/other/logger.dox | 247 | ||||
-rw-r--r-- | docs/doxygen/other/metadata.dox | 350 | ||||
-rw-r--r-- | docs/doxygen/other/msg_passing.dox | 381 | ||||
-rw-r--r-- | docs/doxygen/other/ofdm.dox | 152 | ||||
-rw-r--r-- | docs/doxygen/other/oot_config.dox | 87 | ||||
-rw-r--r-- | docs/doxygen/other/operating_fg.dox | 287 | ||||
-rw-r--r-- | docs/doxygen/other/packet_txrx.dox | 91 | ||||
-rw-r--r-- | docs/doxygen/other/perf_counters.dox | 107 | ||||
-rw-r--r-- | docs/doxygen/other/pfb_intro.dox | 167 | ||||
-rw-r--r-- | docs/doxygen/other/pmt.dox | 506 | ||||
-rw-r--r-- | docs/doxygen/other/prefs.dox | 96 | ||||
-rw-r--r-- | docs/doxygen/other/python_blocks.dox | 163 | ||||
-rw-r--r-- | docs/doxygen/other/stream_tags.dox | 245 | ||||
-rw-r--r-- | docs/doxygen/other/tagged_stream_blocks.dox | 320 | ||||
-rw-r--r-- | docs/doxygen/other/thread_affinity.dox | 132 | ||||
-rw-r--r-- | docs/doxygen/other/usage.dox | 45 | ||||
-rw-r--r-- | docs/doxygen/other/volk_guide.dox | 160 |
17 files changed, 20 insertions, 3516 deletions
diff --git a/docs/doxygen/other/logger.dox b/docs/doxygen/other/logger.dox deleted file mode 100644 index 00387768c0..0000000000 --- a/docs/doxygen/other/logger.dox +++ /dev/null @@ -1,247 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_logger Logging - -\section logging Logging - -GNU Radio has a logging interface to enable various levels of logging -information to be printed to the console or a file. The logger derives -from log4cpp (http://log4cpp.sourceforge.net/) which is readily -available in most Linux distributions. This is an optional dependency -and GNU Radio will work without it. - -Logging is useful for blocks to print out certain amounts of data at -different levels. These levels are: - -<pre> - DEBUG < INFO < WARN < TRACE < ERROR < ALERT < CRIT < FATAL < EMERG -</pre> - - -The order here determines the level of output. These levels are -hierarchical in that specifying any level also includes any level -above it. For example, when using the INFO level, all INFO and -higher messages are logged and DEBUG is ignored. A level NOTSET is provided -to disable a logger. - -\subsection configfile Logging Configuration - -The logging configuration can be found in the gnuradio-runtime.conf file -under the [LOG] section. This allows us fairly complete control over -the logging facilities. The main configuration functions are to set up -the level of the loggers and set the default output behavior of the -loggers. - -There are two default loggers that all gr_block's have access to: -d_logger and d_debug_logger. The first is a standard logger meant to -output simple information about the block while it is running. The -debug logger is meant for debugging purposes and is added to make it -convenient to use a secondary logger that outputs to a different -stream or file. - -The four main configure options are: - -<pre> - log_level = debug - debug_level = debug - log_file = stdout - debug_file = stderr -</pre> - -This establishes the two loggers as having access to all levels of -logging events (DEBUG through EMERG). They are also configured not to -use files but instead output to the console. The standard logger will -output to standard out while the debug logger outputs to standard -error. - -Changing these last two lines to another value will create files that -are used to store the log messages. All messages are appended to the -file. - -When using either standard error or standard out, the messages for the -two different loggers will look like: - -<pre> - gr::log :\<level\>: \<block alias\> - \<message\> - gr::debug :\<level\>: \<block alias\> - \<message\> -</pre> - -When using a file, the only difference in the format is that the -message prefix of "gr::log" or "gr::debug" is not used. Instead, the -time in milliseconds from the start of the program is inserted. - -Remember that a local "~/.gnuradio/config.conf" file can be used to -override any parameter in the global file (see \ref prefs for more -details). - -To use these loggers inside of a GNU Radio block, we use the protected -data members of d_logger and d_debug_logger of gr_block and pass them -to our pre-defined macros: - -\code - GR_LOG_<level>(<logger>, "<Message to print>"); -\endcode - -Where \<level\> is one of the levels as mentioned above, \<logger\> is -either d_logger or d_debug_logger, and \<Message to print\> is the -message we want to output. If we wanted to output an INFO level -message to the standard logger and a WARN level message to the debug -logger, it would look like this: - -\code - GR_LOG_INFO(d_logger, "Some info about the block"); - GR_LOG_WARN(d_debug_logger, "Some warning about the block"); -\endcode - -When this is printed to wherever you are directing the output of the -logger, it will look like: - -<pre> - gr::log :INFO: <block's alias> - Some info about the block - gr::debug :WARN: <block's alias> - Some warning about the block -</pre> - -This provides us information about where the message came from, the -level of the message, and the block that generated the message. We use -the concept of the block's alias which by default (i.e., unless -otherwise set by the user) includes the name of the block and a unique -ID to distinguish it from other blocks of the same type. - -The various logging macros are defined in gr_logger.h. Here are some -simple examples of using them: - -\code - GR_LOG_DEBUG(LOG, "DEBUG message"); - GR_LOG_INFO(LOG, "INFO message"); - GR_LOG_NOTICE(LOG, "NOTICE message"); - GR_LOG_WARN(LOG, "WARNING message"); - GR_LOG_ERROR(LOG, "ERROR message"); - GR_LOG_CRIT(LOG, "CRIT message"); - GR_LOG_ALERT(LOG, "ALERT message"); - GR_LOG_FATAL(LOG, "FATAL message"); - GR_LOG_EMERG(LOG, "EMERG message"); -\endcode - -If the logger is not enabled, then these macros become nops and do -nothing (and d_logger and d_debug_logger are NULL pointers). If -logging is enabled but the log4cpp library is not found, then TRACE, -INFO, and NOTICE levels go to stdout and the rest to stderr. - - -\subsection adv_config Advanced Configuration Options - -If not using the simplified settings discussed above, where we can -direct the logger messages to either a file or one of the standard -outputs, we must use a more complicated configuration file. We do this -by specifying the "log_config" option in the [LOG] section. The -log4cpp documentation will provide more information on how -configuration works and looks. Mostly, a default configuration script -provided with GNU Radio can be used. After installation, the default -configuration script is located at: - -<pre> - $prefix/etc/gnuradio/gr_log_default.conf -</pre> - -For the following examples, we will assume that our local -"~/.gnuradio/config.conf" looks like this: - -\code -[LOG] -log_config = /opt/gr/etc/gnuadio/gr_log_default.conf -log_level = debug -debug_level = Off -\endcode - -Inside of the default configuration file, we define the parameters -for the two logger's, the standard logger the separate debug logger. - -If the levels of the two loggers are specified in our configuration -file, as in the above example, these levels override any levels -specified in the XML file. Here, we have turned on the standard logger -(d_logger) to all levels and turned off the debug logger -(d_debug_logger). So even if the debug logger is used in the code, it -will not actually output any information. Conversely, any level of -output passed to the standard logger will output because we have -turned this value to the lowest level "debug." - -If both an XML configuration file is set and the "log_file" or -"debug_file" options are set at the same time, both systems are -actually used. So you can configure file access and the pattern -through the XML file while also still outputting to stdout or stderr. - - -\section advlog Advanced Usage - -The description above for using the logging facilities is specific to -GNU Radio blocks. We have put the code necessary to access the -debugger into the gr_block parent class to simplify access and make -sure all blocks have the ability to quickly and easily use the logger. - -For non gr_block-based code, we have to get some information about the -logger in order to properly access it. Each logger only exists once as -a singleton in the system, but we need to get a pointer to the right -logger and then set it up for our local use. The following code -snippet shows how to do this to get access to the standard logger, -which has a root of "gr_log." (access to the debug logger is similar -except we would use "gr_log_debug." in the GR_LOG_GETLOGGER call): - -\code - prefs *p = prefs::singleton(); - std::string log_file = p->get_string("LOG", "log_config", ""); - std::string log_level = p->get_string("LOG", "log_level", "off"); - GR_CONFIG_LOGGER(log_file); - GR_LOG_GETLOGGER(LOG, "gr_log." + "my_logger_name"); - GR_LOG_SET_LEVEL(LOG, log_level); -\endcode - -This creates a pointer called LOG (which is instantiated as a -log4cpp:LoggerPtr in the macro) that we can now use locally as the -input to our logging macros like 'GR_LOG_INFO(LOG, "message")'. - -\subsection using_logging Using Logging in Out of Tree Modules - -In order to use the logging interface in an out of tree module based on a -gr_modtool template module, several CMake modifications are required. -Without these changes, logging will be disabled. - -GrMiscUtils.cmake module must be included in the OOT module top level -CMakeLists.Texts file, and the GR_LOGGING() function provided by GrMiscUtils -must be called from the same top level CMakeLists.txt file. This will -set the appropriate build environment and during that process, attempt -to find the log4cpp package using the FindLog4Cpp.cmake module. -This module is not included in the module by gr_modtool, but is part of -the GNU Radio codebase and can be copied directly into the cmake/Modules/ -directory of the OOT module. - -Once these CMake changes are made, the GR logging interface will function -as documented on this page. - -\section logPy Logging from Python - -The logging capability has been brought out python via swig. The configuration -of the logger can be manipulated via the following calls: -\code - from gnuradio import gr - gr.logger_config(filename,watch_period) # Configures the logger with conf file filename - names = gr.logger_get_names() # Returns the names of all loggers - gr.logger_reset_config() # Resets logger config by removing all appenders -\endcode - -Once the logger is configured you can manipulate a logger via a wrapper class gr.logger(). -You can isntantiate this by the following. (Reference logger.h for list of methods) -\code - from gnuradio import gr - log=gr.logger("nameOfLogger") - log.debug("Log a debug message") - log.set_level("INFO"); - -\endcode -*/ diff --git a/docs/doxygen/other/metadata.dox b/docs/doxygen/other/metadata.dox deleted file mode 100644 index b58d2a6aee..0000000000 --- a/docs/doxygen/other/metadata.dox +++ /dev/null @@ -1,350 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_metadata Metadata Information - -\section metadata_introduction Introduction - -Metadata files have extra information in the form of headers that -carry metadata about the samples in the file. Raw, binary files carry -no extra information and must be handled delicately. Any changes in -the system state such as a receiver's sample rate or frequency are -not conveyed with the data in the file itself. Headers solve this problem. - -We write metadata files using gr::blocks::file_meta_sink and read metadata -files using gr::blocks::file_meta_source. - -Metadata files have headers that carry information about a segment of -data within the file. The header structure is described in detail in -the next section. A metadata file always starts with a header that -describes the basic structure of the data. It contains information -about the item size, data type, if it's complex, the sample rate of -the segment, the time stamp of the first sample of the segment, and -information regarding the header size and segment size. - -The first static portion of the header file contains the following -information. - -- version: (char) version number (usually set to METADATA_VERSION) -- rx_rate: (double) Stream's sample rate -- rx_time: (pmt::pmt_t pair - (uint64_t, double)) Time stamp (format from UHD) -- size: (int) item size in bytes - reflects vector length if any -- type: (int) data type (enum below) -- cplx: (bool) true if data is complex -- strt: (uint64_t) start of data relative to current header -- bytes: (uint64_t) size of following data segment in bytes - -An optional extra section of the header stores information in any -received tags. The two main tags associated with headers are: - -- rx_rate: the sample rate of the stream. -- rx_time: the time stamp of the first item in the segment. - -These tags were inspired by the UHD tag format. - -The header gives enough information to process and handle the -data. One cautionary note, though, is that the data type should never -change within a file. There should be very little need for this, because -GNU Radio blocks can only set the data type of their -IO signatures in the constructor, so changes in the data type -afterward will not be recognized. - -We also have an extra header segment that is optional. This can be -loaded up at the beginning by the user specifying some extra metadata -that should be transmitted along with the data. It also grows whenever -it sees a stream tag, so the dictionary will contain any key:value -pairs out of tags from the flowgraph. - - -\subsection metadata_types Types of Metadata Files - -GNU Radio currently supports two types of metadata files: - -- inline: headers are inline with the data in the same file. -- detached: headers are in a separate header file from the data. - -The inline method is the standard version. When a detached header is -used, the headers are simply inserted back-to-back in the detached -header file. The dat file, then, is the standard raw binary format -with no interruptions in the data. - - -\subsection metadata_updating Updating Headers - -While there is always a header that starts a metadata file, they are -updated throughout as well. There are two events that trigger a new -header. We define a segment as the unit of data associated with the -last header. - -The first event that will trigger a new header is when enough samples -have been written for the given segment. This number is defined as the -maximum segment size and is a parameter we pass to the -file_meta_sink. It defaults to 1 million items (items, not -bytes). When that number of items is reached, a new header is -generated and a new segment is started. This makes it easier for us to -manipulate the data later and helps protect against catastrophic data -loss. - -The second event to trigger a new segment is if a new tag is -observed. If the tag is a standard tag in the header, the header value -is updated, the header and current extras are written to file, and the -segment begins again. If a tag from the extras is seen, the value -associated with that tag is updated; and if a new tag is seen, a new -key:value pair are added to the extras dictionary. - -When new tags are seen, we generate a new segment so that we make sure -that all samples in that segment are defined by the header. If the -sample rate changes, we create a new segment where all of the new -samples are at that new rate. Also, in the case of UHD devices, if a -segment loss is observed, it will generate a new timestamp as a tag of -'rx_time'. We create a new file segment that reflects this change to -keep the sample times exact. - - -\subsection metadata_implementation Implementation - -Metadata files are created using gr::blocks::file_meta_sink. The -default behavior is to create a single file with inline headers as -metadata. An option can be set to switch to detached header mode. - -Metadata files are read into a flowgraph using -gr::blocks::file_meta_source. This source reads a metadata file, -inline by default with a settable option to use detached headers. The -data from the segments is converted into a standard streaming -output. The 'rx_rate' and 'rx_time' and all key:value pairs in the -extra header are converted into tags and added to the stream tags -interface. - - -\section metadata_structure Structure - -The file metadata consists of a static mandatory header and a dynamic -optional extras header. Each header is a separate PMT -dictionary. Headers are created by building a PMT dictionary -(pmt::make_dict) of key:value pairs, then the dictionary is -serialized into a string to be written to file. The header is always -the same length that is predetermined by the version of the header -(this must be known already). The header will then indicate if there -is extra data to be extracted as a separate serialized dictionary. - -To work with the PMTs for creating and extracting header information, -we use PMT operators. For example, we create a simplified version of -the header in C++ like this: - -\code - const char METADATA_VERSION = 0x0; - pmt::pmt_t header; - header = pmt::make_dict(); - header = pmt::dict_add(header, pmt::mp("version"), pmt::mp(METADATA_VERSION)); - header = pmt::dict_add(header, pmt::mp("rx_rate"), pmt::mp(samp_rate)); - std::string hdr_str = pmt::serialize_str(header); -\endcode - -The call to pmt::dict_add adds a new key:value pair to the -dictionary. Notice that it both takes and returns the 'header' -variable. This is because we are actually creating a new dictionary -with this function, so we just assign it to the same variable. - -The 'mp' functions are convenience functions provided by the PMT -library. They interpret the data type of the value being inserted and -call the correct 'pmt::from_xxx' function. For more direct control over -the data type, see PMT functions in pmt.h, such as -pmt::from_uint64 or pmt::from_double. - -We finish this off by using pmt::serialize_str to convert the PMT -dictionary into a specialized string format that makes it easy to -write to a file. - -The header is always METADATA_HEADER_SIZE bytes long and a metadata -file always starts with a header. So to extract the header from a -file, we need to read in this many bytes from the beginning of the -file and deserialize it. An important note about this is that the -deserialize function must operate on a std::string. The serialized -format of a dictionary contains null characters, so normal C character -arrays (e.g., 'char *s') get confused. - -Assuming that 'std::string str' contains the full string as read from -a file, we can access the dictionary in C++ like this: - -\code - pmt::pmt_t hdr = pmt::deserialize_str(str); - if(pmt::dict_has_key(hdr, pmt::string_to_symbol("strt"))) { - pmt::pmt_t r = pmt::dict_ref(hdr, pmt::string_to_symbol("strt"), pmt::PMT_NIL); - uint64_t seg_start = pmt::to_uint64(r); - uint64_t extra_len = seg_start - METADATA_HEADER_SIZE; - } -\endcode - -This example first deserializes the string into a PMT dictionary -again. This will throw an error if the string is malformed and cannot -be deserialized correctly. We then want to get access to the item with -key 'strt'. As the next subsection will show, this value indicates at -which byte the data segment starts. We first check to make sure that -this key exists in the dictionary. If not, our header does not contain -the correct information and we might want to handle this as an error. - -Assuming the header is properly formatted, we then get the particular -item referenced by the key 'strt'. This is a uint64_t, so we use the -PMT function to extract and convert this value properly. We now know -if we have an extra header in the file by looking at the difference -between 'seg_start' and the static header size, -METADATA_HEADER_SIZE. If the 'extra_len' is greater than 0, we know we -have an extra header that we can process. Moreover, this also tells us -the size of the serialized PMT dictionary in bytes, so we can easily -read this many bytes from the file. We can then deserialize and parse -this header just like the first. - - -\subsection metadata_header Header Information - -The header is a PMT dictionary with a known structure. This structure -may change, but we version the headers, so all headers of version X -must be the same length and structure. As of now, we only have version -0 headers, which look like the following: - -- version: (char) version number (usually set to METADATA_VERSION) -- rx_rate: (double) Stream's sample rate -- rx_time: (pmt::pmt_t pair - (uint64_t, double)) Time stamp (format from UHD) -- size: (int) item size in bytes - reflects vector length if any. -- type: (int) data type (enum below) -- cplx: (bool) true if data is complex -- strt: (uint64_t) start of data relative to current header -- bytes: (uint64_t) size of following data segment in bytes - -The data types are indicated by an integer value from the following -enumeration type: - -\code -enum gr_file_types { - GR_FILE_BYTE=0, - GR_FILE_CHAR=0, - GR_FILE_SHORT=1, - GR_FILE_INT, - GR_FILE_LONG, - GR_FILE_LONG_LONG, - GR_FILE_FLOAT, - GR_FILE_DOUBLE, -}; -\endcode - -\subsection metadata_extras Extras Information - -The extras section is an optional segment of the header. If 'strt' == -METADATA_HEADER_SIZE, then there is no extras. Otherwise, it is simply -a PMT dictionary of key:value pairs. The extras header can contain -anything and can grow while a program is running. - -We can insert extra data into the header at the beginning if we -wish. All we need to do is use the pmt::dict_add function to insert -our hand-made metadata. This can be useful to add our own markers and -information. - -The main role of the extras header, though, is as a container to hold -any stream tags. When a stream tag is observed coming in, the tag's -key and value are added to the dictionary. Like a standard dictionary, -any time a key already exists, the value will be updated. If the key -does not exist, a new entry is created and the new key:value pair are -added together. So any new tags that the file metadata sink sees will -add to the dictionary. It is therefore important to always check the -'strt' value of the header to see if the length of the extras -dictionary has changed at all. - -When reading out data from the extras, we do not necessarily know the -data type of the PMT value. The key is always a PMT symbol, but the -value can be any other PMT type. There are PMT functions that allow us -to query the PMT to test if it is a particular type. We also have the -ability to do pmt::print on any PMT object to print it to -screen. Before converting from a PMT to its natural data type, it is -necessary to know the data type. - - -\section metadata_utilities Utilities - -GNU Radio comes with a couple of utilities to help in debugging and -manipulating metadata files. There is a general parser in Python that -will convert the PMT header and extra header into Python -dictionaries. This utility is: - -- gr-blocks/python/parse_file_metadata.py - -This program is installed into the Python directory under the -'gnuradio' module, so it can be accessed with: - -\code -from gnuradio.blocks import parse_file_metadata -\endcode - -It defines HEADER_LENGTH as the static length of the metadata header -size. It also has dictionaries that can be used to convert from the -file type to a string (ftype_to_string) and one to convert from the -file type to the size of the data type in bytes (ftype_to_size). - -The 'parse_header' takes in a PMT dictionary, parses it, and returns a -Python dictionary. An optional 'VERBOSE' bool can be set to print the -information to standard out. - -The 'parse_extra_dict' is similar in that it converts from a PMT -dictionary to a Python dictionary. The values are kept in their PMT -format since we do not necessarily know the native data type. - -A program called 'gr_read_file_metadata' is installed into the path -and can be used to read out all header information from a metadata -file. This program is just called with the file name as the first -command-line argument. An option '-D' will handle detached header -files where the file of headers is expected to be the file name of the -data with '.hdr' appended to it. - - -\section metadata_examples Examples - -Examples are located in: - -- gr-blocks/examples/metadata - -Currently, there are a few GRC example programs. - -- file_metadata_sink: create a metadata file from UHD samples. -- file_metadata_source: read the metadata file as input to a simple graph. -- file_metadata_vector_sink: create a metadata file from UHD samples. -- file_metadata_vector_source: read the metadata file as input to a simple graph. - -The file sink example can be switched to use a signal source instead -of a UHD source, but no extra tagged data is used in this mode. - -The file source example pushes the data stream to a new raw file while -a tag debugger block prints out any tags observed in the metadata -file. A QT GUI time sink is used to look at the signal as well. - -The versions with 'vector' in the name are similar except they use -vectors of data. - -The following shows a simple way of creating extra metadata for a -metadata file. This example is just showing how we can insert a date -into the metadata to keep track of it later. The date in this case is -encoded as a vector of uint16 with [day, month, year]. - -\code - import pmt - from gnuradio import blocks - - key = pmt.intern("date") - val = pmt.init_u16vector(3, [13,12,2012]) - - extras = pmt.make_dict() - extras = pmt.dict_add(extras, key, val) - extras_str = pmt.serialize_str(extras) - self.sink = blocks.file_meta_sink(gr.sizeof_gr_complex, - "/tmp/metadat_file.out", - samp_rate, 1, - blocks.GR_FILE_FLOAT, True, - 1000000, extra_str, False) - -\endcode - -*/ diff --git a/docs/doxygen/other/msg_passing.dox b/docs/doxygen/other/msg_passing.dox deleted file mode 100644 index 14de7bae4c..0000000000 --- a/docs/doxygen/other/msg_passing.dox +++ /dev/null @@ -1,381 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_msg_passing Message Passing - -\section msg_passing_introduction Introduction - -GNU Radio was originally a streaming system with no other mechanism to -pass data between blocks. Streams of data are a model that work well -for samples, bits, etc., but are not really the right mechanism for -control data, metadata, or packet structures (at least at -some point in the processing chain). - -We solved part of this problem by introducing the tag stream (see \ref -page_stream_tags). This is a parallel stream to the data -streaming. The difference is that tags are designed to hold metadata -and control information. Tags are specifically associated with a -particular sample in the data stream and flow downstream alongside the -data. This model allows other blocks to identify that an event or -action has occurred or should occur on a particular item. The major -limitation is that the tag stream is really only accessible inside a -work function and only flows in one direction. Its benefit is that it -is isosynchronous with the data. - -We want a more general message passing system for a couple of -reasons. The first is to allow blocks downstream to communicate back -to blocks upstream. The second is to allow an easier way for us to -communicate back and forth between external applications and GNU -Radio. GNU Radio's message passing interface handles these cases, although -it does so on an asynchronous basis. - -The message passing interface heavily relies on Polymorphic Types -(PMTs) in GNU Radio. For further information about these data -structures, see the page \ref page_pmt. - -\section msg_passing_api Message Passing API - -The message passing interface is designed into the gr::basic_block, -which is the parent class for all blocks in GNU Radio. Each block has -a set of message queues to hold incoming messages and can post -messages to the message queues of other blocks. The blocks also -distinguish between input and output ports. - -A block has to declare its input and output message ports in its -constructor. The message ports are described by a name, which is in -practice a PMT symbol (<em>i.e.</em>, an interned string). The API calls -to register a new port are: - -\code - void message_port_register_in(pmt::pmt_t port_id) - void message_port_register_out(pmt::pmt_t port_id) -\endcode - -In Python: - -\code - self.message_port_register_in(pmt.intern("port name")) - self.message_port_register_out(pmt.intern("port name")) -\endcode - -The ports are now identifiable by that port name. Other blocks who may -want to post or receive messages on a port must subscribe to it. When -a block has a message to send, they are published on a particular -port using the following API: - -\code - void message_port_pub(pmt::pmt_t port_id, pmt::pmt_t msg); -\endcode - -In Python: - -\code - self.message_port_pub(pmt.intern("port name"), <pmt message>) -\endcode - -Subscribing is usually done in the form of connecting message ports -as part of the flowgraph, as discussed later. Internally, when message -ports are connected, the gr::basic_block::message_port_sub method is -called. - -Any block that has a subscription to another block's output message -port will receive the message when it is published. Internally, when a -block publishes a message, it simply iterates through all blocks that -have subscribed and uses the gr::basic_block::_post method to send the -message to that block's message queue. - - -\subsection msg_passing_msg_handler Message Handler Functions - -A subscriber block must declare a message handler function to process -the messages that are posted to it. After using the -gr::basic_block::message_port_register_in to declare a subscriber port, we -must then bind this port to the message handler. For this, we use -Boost's 'bind' function: - -\code - set_msg_handler(pmt::pmt_t port_id, - boost::bind(&block_class::message_handler_function, this, _1)); -\endcode - -In Python: - -\code - self.set_msg_handler(pmt.intern("port name"), <msg handler function>) -\endcode - -When a new message is pushed onto a port's message queue, -it is this function that is used to process the message. -The 'port_id' is the same PMT as used when registering the input -port. The 'block_class::message_handler_function' is the member -function of the class designated to handle messages to this port. The -'this' and '_1' are standard ways of using the Boost bind function to -pass the 'this' pointer as the first argument to the class (standard -OOP practice) and the _1 is an indicator that the function expects 1 -additional argument. The prototype for all message handling functions -is: - -\code - void block_class::message_handler_function(pmt::pmt_t msg); -\endcode - -In Python the equivalent function would be: - -\code - def handle_msg(self, msg): -\endcode - -We give examples of using this below. - - -\subsection msg_passing_fg_connect Connecting Messages through the Flowgraph - -From the flowgraph level, we have instrumented a gr::hier_block2::msg_connect -method to make it easy to subscribe blocks to other blocks' -messages. Assume that the block \b src has an output message port named -\a pdus and the block \b dbg has an input port named \a print. The message -connection in the flowgraph (in Python) looks like the following: - -\code - self.tb.msg_connect(src, "pdus", dbg, "print") -\endcode - -All messages published by the \b src block on port \a pdus will be -received by \b dbg on port \a print. Note here how we are just using -strings to define the ports, not PMT symbols. This is a convenience to -the user to be able to more easily type in the port names (for -reference, you can create a PMT symbol in Python using the -pmt::intern function as pmt.intern("string")). - -Users can also query blocks for the names of their input and output -ports using the following API calls: - -\code - pmt::pmt_t message_ports_in(); - pmt::pmt_t message_ports_out(); -\endcode - -The return value for these are a PMT vector filled with PMT symbols, -so PMT operators must be used to manipulate them. - -Each block has internal methods to handle posting and receiving of -messages. The gr::basic_block::_post method takes in a message and -places it into its queue. The publishing model uses the -gr::basic_block::_post method of the blocks as the way to access the -message queue. So the message queue of the right name will have a new -message. Posting messages also has the benefit of waking up the -block's thread if it is in a wait state. So if idle, as soon as a -message is posted, it will wake up and call the message handler. - - -\section msg_passing_posting Posting from External Sources - -An important feature of the message passing architecture -is how it can be used to take in messages from an external source. We -can call a block's gr::basic_block::_post method directly and pass it a -message. So any block with an input message port can receive messages -from the outside in this way. - -The following example uses a gr::blocks::pdu_to_tagged_stream block -as the source block to a flowgraph. Its purpose is to wait for -messages as PDUs posted to it and convert them to a normal stream. The -payload will be sent on as a normal stream while the meta data will be -decoded into tags and sent on the tagged stream. - -So if we have created a \b src block as a PDU to stream, it has a \a -pdus input port, which is how we will inject PDU messages into the -flowgraph. These PDUs could come from another block or flowgraph, but -here, we will create and insert them by hand. - -\code - port = pmt.intern("pdus") - msg = pmt.cons(pmt.PMT_NIL, pmt.make_u8vector(16, 0xFF)) - src.to_basic_block()._post(port, msg) -\endcode - -The PDU's metadata section is empty, hence the pmt::PMT_NIL -object. The payload is now just a simple vector of 16 bytes of all -1's. To post the message, we have to access the block's gr::basic_block -class, which we do using the gr::basic_block::to_basic_block method and -then call the gr::basic_block::_post method to pass the PDU to the -right port. - -All of these mechanisms are explored and tested in the QA code of the -file qa_pdu.py. - -There are some examples of using the message passing infrastructure -through GRC in gr-blocks/examples/msg_passing. - - -\section msg_passing_commands Using messages as commands - -One important use of messages is to send commands to blocks. Examples for this include: - -- gr::qtgui::freq_sink_c: The scaling of the frequency axis can be changed by messages -- gr::uhd::usrp_source and gr::uhd::usrp_sink: Many transceiver-related settings can - be manipulated through command messages, such as frequency, gain and LO offset -- gr::digital::header_payload_demux, which receives an acknowledgement from a header parser - block on how many payload items there are to process - -There is no special PMT type to encode commands, however, it is strongly recommended -to use one of the following formats: - -- pmt::cons(KEY, VALUE): This format is useful for commands that take a single value. - Think of KEY and VALUE as the argument name and value, respectively. For the case of - the QT GUI Frequency Sink, KEY would be "freq" and VALUE would be the new center frequency - in Hz. -- pmt::dict((KEY1: VALUE1), (KEY2: VALUE2), ...): This is basically the same as the - previous format, but you can provide multiple key/value pairs. This is particularly - useful when a single command takes multiple arguments which can't be broken into - multiple command messages (e.g., the USRP blocks might have both a timestamp and a - center frequency in a command message, which are closely associated). - -In both cases, all KEYs should be pmt::symbols (i.e. strings). VALUEs can be -whatever the block requires. - -It might be tempting to deviate from this format, e.g. the QT Frequency sink could -simply take a float value as a command message, and it would still work fine. -However, there are some very good reasons to stick to this format: - -- Interoperability: The more people use the standard format, the more likely it - is that blocks from different sources can work together -- Inspectability: A message debug block will display more useful information about - a message if it's containing both a value and a key -- Intuition: This format is pretty versatile and unlikely to create situations - where it is not sufficient (especially considering that values are PMTs themselves). - As a counterexample, using positional arguments (something like "the first argument - is the frequency, the second the gain") is easily forgotten, or changed in one place - and not another, etc. - - -\section msg_passing_examples Code Examples - -The following is snippets of code from blocks currently in GNU Radio -that take advantage of message passing. We will be using -gr::blocks::message_debug and gr::blocks::tagged_stream_to_pdu below -to show setting up both input and output message passing capabilities. - -The gr::blocks::message_debug block is used for debugging the message -passing system. It describes three input message ports: \a print, \a -store, and \a pdu_print. The \a print port simply prints out all -messages to standard out while the \a store port keeps a list of all -messages posted to it. The \a pdu_print port specially formats PDU -messages for printing to standard out. The \a store port works in -conjunction with a gr::blocks::message_debug::get_message(int i) call -that allows us to retrieve message \p i afterward. - -The constructor of this block looks like this: - -\code -{ - message_port_register_in(pmt::mp("print")); - set_msg_handler(pmt::mp("print"), - boost::bind(&message_debug_impl::print, this, _1)); - - message_port_register_in(pmt::mp("store")); - set_msg_handler(pmt::mp("store"), - boost::bind(&message_debug_impl::store, this, _1)); - - message_port_register_in(pmt::mp("print_pdu")); - set_msg_handler(pmt::mp("print_pdu"), - boost::bind(&message_debug_impl::print_pdu, this, _1)); -} -\endcode - -The three message input ports are registered by their respective -names. We then use the gr::basic_block::set_msg_handler function to -identify this particular port name with a callback function. The -Boost \a bind function (<a target="_blank" -href="http://www.boost.org/doc/libs/1_52_0/libs/bind/bind.html">Boost::bind</a>) -here binds the callback to a function of this block's class. So now -the functions in the block's private implementation class, -gr::blocks::message_debug_impl::print, -gr::blocks::message_debug_impl::store, and -gr::blocks::message_debug_impl::print_pdu, are assigned to handle -messages passed to them. Below is the \a print function for reference. - -\code -void -message_debug_impl::print(pmt::pmt_t msg) -{ - std::cout << "***** MESSAGE DEBUG PRINT ********\n"; - pmt::print(msg); - std::cout << "**********************************\n"; -} -\endcode - -The function simply takes in the PMT message and prints it. The method -pmt::print is a function in the PMT library to print the -PMT in a friendly and (mostly) pretty manner. - -The gr::blocks::tagged_stream_to_pdu block only defines a single -output message port. In this case, its constructor contains the line: - -\code -{ - message_port_register_out(pdu_port_id); -} -\endcode - -So we are only creating a single output port where \a pdu_port_id -is defined in the file pdu.h as \a pdus. - -This block's purpose is to take in a stream of samples along with -stream tags and construct a predefined PDU message from it. In GNU -Radio, we define a PDU as a PMT pair of (metadata, data). The metadata -describes the samples found in the data portion of the -pair. Specifically, the metadata can contain the length of the data -segment and any other information (sample rate, etc.). The PMT vectors -know their own length, so the length value is not actually necessary -unless useful for purposes down the line. The metadata is a PMT -dictionary while the data segment is a PMT uniform vector of either -bytes, floats, or complex values. - -In the end, when a PDU message is ready, the block calls its -gr::blocks::tagged_stream_to_pdu_impl::send_message function that is -shown below. - -\code -void -tagged_stream_to_pdu_impl::send_message() -{ - if(pmt::length(d_pdu_vector) != d_pdu_length) { - throw std::runtime_error("msg length not correct"); - } - - pmt::pmt_t msg = pmt::cons(d_pdu_meta, - d_pdu_vector); - message_port_pub(pdu_port_id, msg); - - d_pdu_meta = pmt::PMT_NIL; - d_pdu_vector = pmt::PMT_NIL; - d_pdu_length = 0; - d_pdu_remain = 0; - d_inpdu = false; -} -\endcode - -This function does a bit of checking to make sure the PDU is OK as -well as some cleanup in the end. But it is the line where the message -is published that is important to this discussion. Here, the block -posts the PDU message to any subscribers by calling -gr::basic_block::message_port_pub publishing method. - -There is similarly a gr::blocks::pdu_to_tagged_stream block that essentially -does the opposite. It acts as a source to a flowgraph and waits for -PDU messages to be posted to it on its input port \a pdus. It extracts -the metadata and data and processes them. The metadata dictionary is -split up into key:value pairs and stream tags are created out of -them. The data is then converted into an output stream of items and -passed along. The next section describes how PDUs can be passed into a -flowgraph using the gr::blocks::pdu_to_tagged_stream block. - -For a Python block example, see \ref pyblocks_msgs. - -*/ diff --git a/docs/doxygen/other/ofdm.dox b/docs/doxygen/other/ofdm.dox deleted file mode 100644 index 77d80a7441..0000000000 --- a/docs/doxygen/other/ofdm.dox +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_ofdm OFDM - -\section ofdm_introduction Introduction - -GNU Radio provides some blocks to transmit and receive OFDM-modulated signals. -In the following, we assume the reader is familiar with OFDM and how it works, -for an introduction to OFDM refer to standard textbooks on digital communication. - -The blocks are designed in a very generic fashion. As a developer, this means that -often, a desired functionality can be achieved by correct parametrization of the -available blocks, but in some cases, custom blocks have to be included. The design -of the OFDM components is such that adding own functionality is possible with -very little friction. - -\ref page_packet_data has an example of how to use OFDM in a packet-based -receiver. - -\section ofdm_conventions Conventions and Notations - -\subsection ofdm_fftshift FFT Shifting - -In all cases where OFDM symbols are passed between blocks, the default behaviour -is to FFT-Shift these symbols, i.e. that the DC carrier is in the middle (to be -precise, it is on carrier \f$\lfloor N/2 \rfloor\f$ where N is the FFT length and -carrier indexing starts at 0). - -The reason for this convention is that some blocks require FFT-shifted ordering -of the symbols to function (such as gr::digital::ofdm_chanest_vcvc), and for -consistency's sake, this was chosen as a default for all blocks that pass OFDM -symbols. Also, when viewing OFDM symbols, FFT-shifted symbols are in their -natural order, i.e. as they appear in the pass band. - -\subsection ofdm_indexing Carrier Indexing - -Carriers are always index starting at the DC carrier, which has the index 0 -(you usually don't want to occupy this carrier). The carriers right of the -DC carrier (the ones at higher frequencies) are indexed with 1 through N/2-1 -(N being the FFT length again). - -The carriers left of the DC carrier (with lower frequencies) can be indexed --N/2 through -1 or N/2 through N-1. Carrier indices N-1 and -1 are thus -equivalent. The advantage of using negative carrier indices is that the -FFT length can be changed without changing the carrier indexing. - -\subsection ofdm_carrieralloc Carrier and Symbol Allocation - -Many blocks require knowledge of which carriers are allocated, and whether they -carry data or pilot symbols. GNU Radio blocks uses three objects for this, typically -called \p occupied_carriers (for the data symbols), \p pilot_carriers and -\p pilot_symbols (for the pilot symbols). - -Every one of these objects is a vector of vectors. \p occupied_carriers and -\p pilot_carriers identify the position within a frame where data and pilot -symbols are stored, respectively. - -\p occupied_carriers[0] identifies which carriers are occupied on the first -OFDM symbol, \p occupied_carriers[1] does the same on the second OFDM symbol etc. - -Here's an example: -\code - occupied_carriers = ((-2, -1, 1, 3), (-3, -1, 1, 2)) - pilot_carriers = ((-3, 2), (-2, 3)) -\endcode -Every OFDM symbol carries 4 data symbols. On the first OFDM symbol, they are on carriers -2, -1, 1 and 3. -Carriers -3 and 2 are not used, so they are where the pilot symbols can be placed. -On the second OFDM symbol, the occupied carriers are -3, -1, 1 and 2. The pilot -symbols must thus be placed elsewhere, and are put on carriers -2 and 3. - -If there are more symbols in the OFDM frame than the length of \p occupied_carriers -or \p pilot_carriers, they wrap around (in this example, the third OFDM symbol -uses the allocation in \p occupied_carriers[0]). - -But how are the pilot symbols set? This is a valid parametrization: -\code - pilot_symbols = ((-1, 1j), (1, -1j), (-1, 1j), (-1j, 1)) -\endcode - -The position of these symbols are thos in \p pilot_carriers. So on the first OFDM -symbol, carrier -3 will transmit a -1, and carrier 2 will transmit a 1j. -Note that \p pilot_symbols is longer than \p pilot_carriers in this example-- -this is valid, the symbols in \p pilot_symbols[2] will be mapped according -to \p pilot_carriers[0]. - -\section ofdm_detectsync Detection and Synchronisation - -Before anything happens, an OFDM frame must be detected, the beginning of OFDM -symbols must be identified, and frequency offset must be estimated. - -\section ofdm_tx Transmitting - -\image html ofdm_tx_core.png "Core elements of an OFDM transmitter" - -This image shows a very simple example of a transmitter. It is assumed that the -input is a stream of complex scalars with a length tag, i.e. the transmitter -will work on one frame at a time. - -The first block is the carrier allocator (gr::digital::ofdm_carrier_allocator_cvc). -This sorts the incoming complex scalars onto OFDM carriers, and also places the -pilot symbols onto the correct positions. -There is also the option to pass OFDM symbols which are prepended in front of every -frame (i.e. preamble symbols). These can be used for detection, synchronisation -and channel estimation. - -The carrier allocator outputs OFDM symbols (i.e. complex vectors of FFT length). -These must be converted to time domain signals before continuing, which is why -they are piped into an (I)FFT block. Note that because all the OFDM symbols are -treated in the shifted form, the IFFT block must be shifting as well. - -Finally, the cyclic prefix is added to the OFDM symbols. The gr::digital::ofdm_cyclic_prefixer -can also perform pulse shaping on the OFDM symbols (raised cosine flanks in the -time domain). - -\section ofdm_rx Receiving - -On the receiver side, some more effort is necessary. The following flow graph -assumes that the input starts at the beginning of an OFDM frame and is prepended -with a Schmidl & Cox preamble for coarse frequency correction and channel -estimation. Also assumed is that the fine frequency offset is already corrected -and that the cyclic prefix has been removed. The latter can be achieved by a -gr::digital::header_payload_demux, the former can be done using a -gr::digital::ofdm_sync_sc_cc. - -\image html ofdm_rx_core.png "Core elements of an OFDM receiver" - -First, an FFT shifts the OFDM symbols into the frequency domain, where the signal -processing is performed (the OFDM frame is thus in the memory in matrix form). -It is passed to a block that uses the preambles to perform channel estimation -and coarse frequency offset. Both of these values are added to the output stream -as tags; the preambles are then removed from the stream and not propagated. - -Note that this block does not correct the OFDM frame. Both the coarse frequency -offset correction and the equalizing (using the initial channel state estimate) -are done in the following block, gr::digital::ofdm_frame_equalizer_vcvc. -The interesting property about this block is that it uses a -gr::digital::ofdm_equalizer_base derived object to perform the actual equalization. - -The last block in the frequency domain is the gr::digital::ofdm_serializer_vcc, -which is the inverse block to the carrier allocator. -It plucks the data symbols from the \p occupied_carriers and outputs them as a -stream of complex scalars. These can then be directly converted to bits, or passed -to a forward error correction decoder. - -*/ diff --git a/docs/doxygen/other/oot_config.dox b/docs/doxygen/other/oot_config.dox deleted file mode 100644 index d07c7a2808..0000000000 --- a/docs/doxygen/other/oot_config.dox +++ /dev/null @@ -1,87 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_oot_config Out-of-Tree Configuration - -New as of 3.6.5. - -Using gr_modtool, each package comes with the ability to easily locate -the gnuradio-runtime library using the 'find_package(GnuradioRuntime)' -cmake command. This only locates the gnuradio-runtime library and -include directory, which is enough for most simple projects. - -As projects become more complicated and start needing to rely on other -GNU Radio components like gnuradio-blocks or gnuradio-filter, for -example, and when they become dependent on certain API compatibility -versions of GNU Radio, we need something more. And so we have -introduced the GnuradioConfig.cmake file. - -When GNU Radio is installed, it also installs a GNU Radio-specific -cmake config file that we can use for more advanced compatibility -issues of our projects. This tool allows us to specific the API -compatible version and a set of components that are required. - -Taking the above example, say we have built against version 3.6.5 with -features that were introduced in this version and we need the blocks -and filter components as well as the main core library. We fist set a -cmake variable GR_REQUIRED_COMPONENTS to the components we need. We -then use the 'find_package' command and also set a minimum required -API compatible version. Since we are on the 3.6 API version, the -minimum required version is "3.6.5". The code in the CMakeLists.txt -file would look like this: - -\code - set(GR_REQUIRED_COMPONENTS RUNTIME BLOCKS FILTER) - find_package(Gnuradio 3.6.5) -\endcode - -Note that the capitalization is important on both lines. - -If the installed version of GNU Radio is 3.6.4 or some other API -version like 3.5 or 3.7, the Cmake configuration will fail with the -version error. Likewise, if libgnuradio-filter was not installed as -part of GNU Radio, the configuration will also fail. - -\section oot_config_path_page Install Path - -Cmake has to know where to find either the package config files or the -GnuradioConfig.cmake script. The package config files are located in -$prefix/lib/pkgconfig while all of the Cmake scripts from GNU Radio -are installed into $prefix/lib/cmake/gnuradio. - -If the installed GNU Radio $prefix is '/usr' or '/usr/local', then -everything should work fine. If the GNU Radio install $prefix is -something else, then Cmake must be told where to find it. This can be -done in a few ways: - -1. If you are installing the out-of-tree module into the same $prefix, -then you would be setting '-DCMAKE_INSTALL_PREFIX' on the -configuration command line. This is enough to tell Cmake where to look -for the configuration files. - -2. Cmake will try to find the package config (*.pc) files. If it can, -these files will instruct Cmake where to look for the rest of the -configuration options. If this is not set, it can be set as: - -\code - export PKG_CONFIG_PATH=$prefix/lib/pkgconfg:$PKG_CONFIG_PATH -\endcode - -3. Set the CMAKE_PREFIX_PATH environmental variable to $prefix. - -\code - export CMAKE_PREFIX_PATH=$prefix:$CMAKE_PREFIX_PATH -\endcode - - -With method 1, you will be installing your OOT project into the same -$prefix as GNU Radio. With methods 2 and 3, you can install your -component anywhere you like (using -DCMAKE_INSTALL_PREFIX). - -*/ diff --git a/docs/doxygen/other/operating_fg.dox b/docs/doxygen/other/operating_fg.dox deleted file mode 100644 index 62cc56fd4e..0000000000 --- a/docs/doxygen/other/operating_fg.dox +++ /dev/null @@ -1,287 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_operating_fg Handling flow graphs - -\section flowgraph Operating a Flowgraph - -The basic data structure in GNU Radio is the flowgraph, which -represents the connections of the blocks through which a continuous -stream of samples flows. The concept of a flowgraph is an acyclic -directional graph with one or more source blocks (to insert samples -into the flowgraph), one or more sink blocks (to terminate or export -samples from the flowgraph), and any signal processing blocks in -between. - -A program must at least create a GNU Radio 'top_block', which -represents the top-most structure of the flowgraph. The top blocks -provide the overall control and hold methods such as 'start,' 'stop,' -and 'wait'. - -The general construction of a GNU Radio application is to create a -gr_top_block, instantiate the blocks, connect the blocks together, and -then start the gr_top_block. The following program shows how this is -done. A single source and sink are used with a FIR filter between -them. - -\code - from gnuradio import gr, blocks, filter, analog - - class my_topblock(gr.top_block): - def __init__(self): - gr.top_block.__init__(self) - - amp = 1 - taps = filter.firdes.low_pass(1, 1, 0.1, 0.01) - - self.src = analog.noise_source_c(analog.GR_GAUSSIAN, amp) - self.flt = filter.fir_filter_ccf(1, taps) - self.snk = blocks.null_sink(gr.sizeof_gr_complex) - - self.connect(self.src, self.flt, self.snk) - - if __name__ == "__main__": - tb = my_topblock() - tb.start() - tb.wait() -\endcode - -The 'tb.start()' starts the data flowing through the flowgraph while -the 'tb.wait()' is the equivalent of a thread's 'join' operation and -blocks until the gr_top_block is done. - -An alternative to using the 'start' and 'wait' methods, a 'run' method is -also provided for convenience that is a blocking start call; -equivalent to the above 'start' followed by a 'wait.' - - -\subsection latency Latency and Throughput - -By default, GNU Radio runs a scheduler that attempts to optimize -throughput. Using a dynamic scheduler, blocks in a flowgraph pass -chunks of items from sources to sinks. The sizes of these chunks will -vary depending on the speed of processing. For each block, the number -of items it can process is dependent on how much space it has in its -output buffer(s) and how many items are available on the input -buffer(s). - -The consequence of this is that often a block may be called with a very -large number of items to process (several thousand). In terms of -speed, this is efficient since now the majority of the processing time -is taken up with processing samples. Smaller chunks mean more calls -into the scheduler to retrieve more data. The downside to this is that -it can lead to large latency while a block is processing a large chunk -of data. - -To combat this problem, the gr_top_block can be passed a limit on the -number of output items a block will ever receive. A block may get less -than this number, but never more, and so it serves as an upper limit -to the latency any block will exhibit. By limiting the number of items -per call to a block, though, we increase the overhead of the -scheduler, and so reduce the overall efficiency of the application. - -To set the maximum number of output items, we pass a value into the -'start' or 'run' method of the gr_top_block: - -\code - tb.start(1000) - tb.wait() -or - tb.run(1000) -\endcode - -Using this method, we place a global restriction on the size of items -to all blocks. Each block, though, has the ability to overwrite this -with its own limit. Using the 'set_max_noutput_items(m)' method for an -individual block will overwrite the global setting. For example, in -the following code, the global setting is 1000 items max, except for -the FIR filter, which can receive up to 2000 items. - -\code - tb.flt.set_max_noutput_items(2000) - tb.run(1000) -\endcode - -In some situations, you might actually want to restrict the size of -the buffer itself. This can help to prevent a buffer who is blocked -for data from just increasing the amount of items in its buffer, which -will then cause an increased latency for new samples. You can set the -size of an output buffer for each output port for every block. - -WARNING: This is an advanced feature in GNU Radio and should not be -used without a full understanding of this concept as explained below. - -To set the output buffer size of a block, you simply call: - -\code - tb.blk0.set_max_output_buffer(2000) - tb.blk1.set_max_output_buffer(1, 2000) - tb.start() - print tb.blk1.max_output_buffer(0) - print tb.blk1.max_output_buffer(1) -\endcode - -In the above example, all ports of blk0 are set to a buffer size of -2000 in _items_ (not bytes), and blk1 only sets the size for output -port 1, any and all other ports use the default. The third and fourth -lines just print out the buffer sizes for ports 0 and 1 of blk1. This -is done after start() is called because the values are updated based -on what is actually allocated to the block's buffers. - -NOTES: - -1. Buffer length assignment is done once at runtime (i.e., when run() -or start() is called). So to set the max buffer lengths, the -set_max_output_buffer calls must be done before this. - -2. Once the flowgraph is started, the buffer lengths for a block are -set and cannot be dynamically changed, even during a -lock()/unlock(). If you need to change the buffer size, you will have -to delete the block and rebuild it, and therefore must disconnect and -reconnect the blocks. - -3. This can affect throughput. Large buffers are designed to improve -the efficiency and speed of the program at the expense of -latency. Limiting the size of the buffer may decrease performance. - -4. The real buffer size is actually based on a minimum granularity of -the system. Typically, this is a page size, which is typically 4096 -bytes. This means that any buffer size that is specified with this -command will get rounded up to the nearest granularity (e.g., page size). -When calling max_output_buffer(port) after the flowgraph is -started, you will get how many items were actually allocated in the -buffer, which may be different than what was initially specified. - - -\section reconfigure Reconfiguring Flowgraphs - -It is possible to reconfigure the flowgraph at runtime. The -reconfiguration is meant for changes in the flowgraph structure, not -individual parameter settings of the blocks. For example, changing the -constant in a gr::blocks::add_const_cc block can be done while the flowgraph is -running using the 'set_k(k)' method. - -Reconfiguration is done by locking the flowgraph, which stops it from -running and processing data, performing the reconfiguration, and then -restarting the graph by unlocking it. - -The following example code shows a graph that first adds two -gr::analog::noise_source_c blocks and then replaces the -gr::blocks::add_cc block with a gr::blocks::sub_cc block to then -subtract the sources. - -\code -from gnuradio import gr, analog, blocks -import time - -class mytb(gr.top_block): - def __init__(self): - gr.top_block.__init__(self) - - self.src0 = analog.noise_source_c(analog.GR_GAUSSIAN, 1) - self.src1 = analog.noise_source_c(analog.GR_GAUSSIAN, 1) - self.add = blocks.add_cc() - self.sub = blocks.sub_cc() - self.head = blocks.head(gr.sizeof_gr_complex, 1000000) - self.snk = blocks.file_sink(gr.sizeof_gr_complex, "output.32fc") - - self.connect(self.src0, (self.add,0)) - self.connect(self.src1, (self.add,1)) - self.connect(self.add, self.head) - self.connect(self.head, self.snk) - -def main(): - tb = mytb() - tb.start() - time.sleep(0.01) - - # Stop flowgraph and disconnect the add block - tb.lock() - tb.disconnect(tb.add, tb.head) - tb.disconnect(tb.src0, (tb.add,0)) - tb.disconnect(tb.src1, (tb.add,1)) - - # Connect the sub block and restart - tb.connect(tb.sub, tb.head) - tb.connect(tb.src0, (tb.sub,0)) - tb.connect(tb.src1, (tb.sub,1)) - tb.unlock() - - tb.wait() - -if __name__ == "__main__": - main() -\endcode - -During reconfiguration, the maximum noutput_items value can be changed -either globally using the 'set_max_noutput_items(m)' on the gr_top_block -object or locally using the 'set_max_noutput_items(m)' on any given -block object. - -A block also has a 'unset_max_noutput_items()' method that unsets the -local max noutput_items value so that block reverts back to using the -global value. - -The following example expands the previous example but sets and resets -the max noutput_items both locally and globally. - -\code -from gnuradio import gr, analog, blocks -import time - -class mytb(gr.top_block): - def __init__(self): - gr.top_block.__init__(self) - - self.src0 = analog.noise_source_c(analog.GR_GAUSSIAN, 1) - self.src1 = analog.noise_source_c(analog.GR_GAUSSIAN, 1) - self.add = blocks.add_cc() - self.sub = blocks.sub_cc() - self.head = blocks.head(gr.sizeof_gr_complex, 1000000) - self.snk = blocks.file_sink(gr.sizeof_gr_complex, "output.32fc") - - self.connect(self.src0, (self.add,0)) - self.connect(self.src1, (self.add,1)) - self.connect(self.add, self.head) - self.connect(self.head, self.snk) - -def main(): - # Start the gr_top_block after setting some max noutput_items. - tb = mytb() - tb.src1.set_max_noutput_items(2000) - tb.start(100) - time.sleep(0.01) - - # Stop flowgraph and disconnect the add block - tb.lock() - - tb.disconnect(tb.add, tb.head) - tb.disconnect(tb.src0, (tb.add,0)) - tb.disconnect(tb.src1, (tb.add,1)) - - # Connect the sub block - tb.connect(tb.sub, tb.head) - tb.connect(tb.src0, (tb.sub,0)) - tb.connect(tb.src1, (tb.sub,1)) - - # Set new max_noutput_items for the gr_top_block - # and unset the local value for src1 - tb.set_max_noutput_items(1000) - tb.src1.unset_max_noutput_items() - tb.unlock() - - tb.wait() - -if __name__ == "__main__": - main() -\endcode - - -*/ - diff --git a/docs/doxygen/other/packet_txrx.dox b/docs/doxygen/other/packet_txrx.dox deleted file mode 100644 index 31417779c0..0000000000 --- a/docs/doxygen/other/packet_txrx.dox +++ /dev/null @@ -1,91 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_packet_data Packet Data Transmission - -\section packet_data_introduction Introduction - -In many cases, the PHY layer of a digital transceiver uses <em>packets</em>to break -down the transmission (as opposed to continuously broadcasting data), and GNU Radio -natively supports this kind of transmission. The basic mechanisms which allow this -are the \ref page_msg_passing and the \ref page_tagged_stream_blocks. - -With the tools provided in GNU Radio, simple digital packet transmission schemes -are easily implemented, allowing the creation of packet-based communication links -and even -networks. - -\section packet_data_structure Structure of a packet - -Typically, a packet consists of the following elements: - -- A preamble. This is used for detection, synchronization (in time and frequency) and possibly initial channel state estimation. -- A header. This is of fixed length and stores information about the packet, such as (most importantly) its length, but potentially other information, such as the packet number, its intended recipient etc. -- The payload. -- A checksum, typically a CRC value, to validate the packet contents. - -At the transmitter stage, these are modulated and prepared for transmission (a forward error correction code may also be applied). Because the transmitter knows the packet length, is a trivial matter to create the transmit frames using the tagged stream blocks. - -The receiver has to perform a multitude of things to obtain the packet again. -Most importantly, it has to convert an infinite stream (coming from the receiver -device, e.g. a UHD source block) into a packetized, tagged stream. - -\section packet_data_hpdemuxer The Header/Payload Demuxer and header parser - -The key element to return back to packetized state is the gr::digital::header_payload_demux. -At its first input, it receives a continuous stream of sample data, coming from -the receiver device. It discards all the incoming samples, until the beginning -of a packet is signalled, either by a stream tag, or a trigger signal on its second input. - -Once such a beginning is detected, the demultiplexer copies the preamble and header -to the first output (it must know the exact length of these elements). The header -can then be demodulated with any suitable set of GNU Radio blocks. - -To turn the information stored in the demodulated header bits into metadata -which can be understood by GNU Radio, a gr::digital::packet_headerparser_b block -is used to turn the header data into a message, which is passed back to the -header/payload demuxer. The latter then knows the length of the payload, and -passes that out on the second output, along with all the metadata obtained -in the header demodulation chain. - -The knowledge of the header structure (i.e. how to turn a sequence of bits into -a payload length etc.) is stored in an object of type gr::digital::packet_header_default. -This must be passed to the header parser block. - -\section packet_data_ofdm Packet receiver example: OFDM - -\image html example_ofdm_packet_rx.png "Example: OFDM Packet Receiver" - -The image above shows an example of a simple OFDM receiver. It has no forward error correction, -and uses the simplest possible frame structure. - -The four elements of the packet receiver are highlighted. The first part is the packet detector -and synchronizer. The samples piped into the header/payload demuxer are fine frequency corrected, -and a trigger signal is sent to mark the beginning of the burst. - -Next, the header demodulation receiver chain is activated. The FFT shifts OFDM symbols to the -frequency domain. The coarse frequency offset and initial channel state are estimated from the -preamble and are added to the stream as tags. The OFDM symbol containing the header is passed -to an equalizer, which also corrects the coarse frequency offset. A serializer picks the data -symbols from the OFDM symbol and outputs them as a sequence of scalar complex values, which -are then demodulated into bits (in this case BPSK is used). - -The bits are interpreted by the header parser, which uses a gr::digital::packet_header_ofdm -object to interpret the bits (the latter is derived form gr::digital::packet_header_default). -The result from the header parser is then fed back to the demuxer, which now knows the length of -payload and outputs that as a tagged stream. - -The payload demodulation chain is the same as the header demodulation chain, only the -channel estimator block is unnecessary as the channel state information is still available -as metadata on the payload tagged stream. - -This flow graph, as well as the corresponding transmitter, can be found in -gr-digital/examples/ofdm/rx_ofdm.grc and gr-digital/examples/ofdm/tx_ofdm.grc - - -*/ diff --git a/docs/doxygen/other/perf_counters.dox b/docs/doxygen/other/perf_counters.dox deleted file mode 100644 index 7562b11298..0000000000 --- a/docs/doxygen/other/perf_counters.dox +++ /dev/null @@ -1,107 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_perf_counters Performance Counters - -\section pc_introduction Introduction - -Each block can have a set of Performance Counters that the schedule -keeps track of. These counters measure and store information about -different performance metrics of their operation. The concept is -fairly extensible, but currently, GNU Radio defines the following -Performance Counters: - -\li noutput_items: number of items the block can produce. -\li nproduced: the number of items the block produced. -\li input_buffers_full: % of how full each input buffer is. -\li output_buffers_full: % of how full each output buffer is. -\li work_time: number of CPU ticks during the call to general_work(). -\li work_time_total: Accumulated sum of work_time. - -For each Performance Counter except the work_time_total, we can -retrieve the instantaneous, average, and variance from the -block. Access to these counters is done through a simple set of -functions added to every block in the flowgraph: - -\code - float pc_<name>[_<type>](); -\endcode - -In the above, the \<name\> field is one of the counters in the above -list of counters. The optional \<type\> suffix is either 'avg' to get -the average value or 'var' to get the variance. Without a suffix, the -function returns the most recent instantaneous value. - -We can also reset the Performance Counters back to zero to remove any -history of the current average and variance calculations for a -particular block. - -\code - void reset_perf_counters(); -\endcode - - -\section pc_config Compile-time and Run-time Configuration - -Because the Performance Counters are calculated during each call to -work for every block, they increase the computational cost and memory -overhead. The more blocks used, the more impact this may have. So -while it turns out after some experimentation that the Performance -Counters add very little overhead (less than 1% speed degradation for -a 24-block flowgraph), we err on the side of minimizing overhead in -the scheduler. To do so, we have added compile-time and run-time -configuration of the use of Performance Counters. - - -\subsection pc_config_compile Compile-time Config - -By default, GNU Radio will build without Performance Counters -enabled. To enable Performance Counters, we pass the following flag to -cmake: - -\code - -DENABLE_PERFORMANCE_COUNTERS=True -\endcode - -Note that this affects the GNU Radio block class and the scheduler -itself. Out-of-tree projects will inherit directly from GNU Radio -because of the inheritance with gr::block. Turning on Performance -Counters for GNU Radio will require a recompilation of the OOT project -but no extra configuration. - - -\subsection pc_config_runtime Run-time Config - -Given the Performance Counters are enabled in GNU Radio at -compile-time, we can still control if they are used or not at -run-time. For this, we use the GNU Radio preferences file in the -section [PerfCounters]. This section is installed into the -gnuradio-runtime.conf file. As usual with the preferences, this -section or any of the individual options can be overridden in the -user's config.conf file or using a GR_CONF_ environmental variable -(see \ref prefs for more details). - -The options for the [PerfCounters] section are: - -\li on: Turn counters on/off at run-time. -\li export: Allow counters to be exported over ControlPort. -\li clock: sets the type of clock used when calculating work_time -('thread' or 'monotonic'). - - -\section pc_perfmonitor Performance Monitor - -See \ref perfmonitor for some details of using a ControlPort-based -monitor application, gr-perf-monitorx, for visualizing the -counters. This application is particularly useful in learning which -blocks are the computationally complex blocks that could use extra -optimization or work to improve their performance. It can also be used -to understand the current 'health' of the application. - -*/ diff --git a/docs/doxygen/other/pfb_intro.dox b/docs/doxygen/other/pfb_intro.dox deleted file mode 100644 index 66c9891d75..0000000000 --- a/docs/doxygen/other/pfb_intro.dox +++ /dev/null @@ -1,167 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_pfb Polyphase Filterbanks - -\section pfb_introduction Introduction - -Polyphase filterbanks (PFB) are a very powerful set of filtering tools -that can efficiently perform many multi-rate signal processing -tasks. GNU Radio has a set of polyphase filterbank blocks to be used -in all sorts of applications. - -\section pfb_Usage - -See the documentation for the individual blocks for details about what -they can do and how they should be used. Furthermore, there are -examples for these blocks in <b>gr-filter/examples</b>. - -The main issue when using the PFB blocks is defining the prototype -filter, which is passed to all of the blocks as a vector of \p -taps. The taps from the prototype filter which get partitioned among -the \p N channels of the channelizer. - -An example of creating a set of filter taps for a PFB channelizer is -found on line 49 of <b>gr-filter/examples/channelizer.py</b> -and reproduced below. Notice that the sample rate is the sample rate -at the input to the channelizer while the bandwidth and transition -width are defined for the channel bandwidths. This makes a fairly long -filter that is then split up between the \p N channels of the PFB. - -\code - self._fs = 9000 # input sample rate - self._M = 9 # Number of channels to channelize - - self._taps = filter.firdes.low_pass_2(1, self._fs, 475.50, 50, - attenuation_dB=100, - window=filter.firdes.WIN_BLACKMAN_hARRIS) -\endcode - -In this example, the signal into the channelizer is sampled at 9 ksps -(complex, so 9 kHz of bandwidth). The filter uses 9 channels, so each -output channel will have a bandwidth and sample rate of 1 kHz. We want -to pass most of the channel, so we define the channel bandwidth to be -a low pass filter with a bandwidth of 475.5 Hz and a transition -bandwidth of 50 Hz, but we have defined this using a sample rate of -the original 9 kHz. The prototype filter has 819 taps to be divided up -between the 9 channels, so each channel uses 91 taps. This is probably -over-kill for a channelizer, and we could reduce the amount of taps -per channel to a couple of dozen with no ill effects. - -The basic rule when defining a set of taps for a PFB block is to think -about the filter running at the highest rate it will see while the -bandwidth is defined for the size of the channels. In the channelizer -case, the highest rate is defined as the rate of the incoming signal, -but in other PFB blocks, this is not so obvious. - -Two very useful blocks to use are the arbitrary resampler and the -clock synchronizer (for PAM signals). These PFBs are defined with a -set number of filters based on the fidelity required from them, not -the rate changes. By default, the \p filter_size is set to 32 for -these blocks, which is a reasonable default for most tasks. Because -the PFB uses this number of filters in the filterbank, the maximum -rate of the bank is defined from this (see the theory of a polyphase -interpolator for a justification of this). So the prototype filter is -defined to use a sample rate of \p filter_size times the signal's -sampling rate. - -A helpful wrapper for the arbitrary resampler is found in -<b>gr-filter/python/pfb.py</b>, -which is exposed in Python as <b>filter.pfb.arb_resampler_ccf</b> and -<b>filter.pfb.arb_resampler_fff</b>. This block is set up so that the -user only needs to pass it the real number \p rate as the resampling -rate. With just this information, this hierarchical block -automatically creates a filter that fully passes the signal bandwidth -being resampled but does not pass any out-of-band noise. See the code -for this block for details of how the filter is constructed. - -Of course, a user can create his or her own taps and use them in the -arbitrary resampler for more specific requirements. Some of the UHD -examples (<b>gr-uhd/examples</b>) use this ability to create a -received matched filter or channel filter that also resamples the -signal. - -\section pfb_examples Examples - -The following is an example of the using the channelizer. It creates -the appropriate filter to channelizer 9 channels out of an original -signal that is 9000 Hz wide, so each output channel is now 1000 -Hz. The code then plots the PSD of the original signal to see the -signals in the origina spectrum and then makes 9 plots for each of the -channels. - -NOTE: you need the Matplotlib Python module installed to -run this example. - -\include gr-filter/examples/channelize.py - - -\section pfb_arb_resampler The PFB Arbitrary Resampler Kernel - -GNU Radio has a PFB arbitrary resampler block that can be used to -resample a signal to any arbitrary and real resampling rate. The -resampling feature is one that could easily be useful to other blocks, -and so we have extracted the kernel of the resampler into its own -class that can be used as such. - -The PFB arbitrary resampler is defined in pfb_arb_resampler.h and has -the following constructor: - -\code -namespace gr { - namespace filter { - namespace kernel { - - pfb_arb_resampler_XXX(float rate, - const std::vector<float> &taps, - unsigned int filter_size); - - } /* namespace kernel */ - } /* namespace filter */ -} /* namespace gr */ -\endcode - -Currently, only a 'ccf' and 'fff' version are defined. This kernel, -like the block itself, takes in the resampling \p rate as a floating -point number. The \p taps are passed as the baseband prototype filter, -and the quantization error of the filter is determined by the \p -filter_size parameter. - -The prototype taps are generated like all other PFB filter -taps. Specifically, we construct them generally as a lowpass filter at -the maximum rate of the filter. In the case of these resamplers, the -maximum rate is actually the number of filters. - -A simple example follows. We construct a filter that will pass the -entire passband of the original signal to be resampled. To make it -easy, we work in normalized sample rates for this. The gain of the -filter is set to filter_size to compensate for the upsampling, the -sampling rate itself is also set to filter_size, which is assuming -that the incoming signal is at a sampling rate of 1.0. We defined the -passband to be 0.5 to pass the entire width of the original signal and -set a transition band to 0.1. Note that this causes a bit of roll-off -outside of the original passband and could lead to introducing some -aliasing. More care should be taken to construct the passband and -transition width of the filter for the given signal while keeping the -total number of taps small. A stopband attenuation of 60 dB was used -here, and again, this is a parameter we can adjust to alter the -performance and size of the filter. - -\code - firdes.low_pass_2(filter_size, filter_size, 0.5, 0.1, 60) -\endcode - -As is typical with the PFB filters, a filter size of 32 is generally -an appropriate trade-off of accuracy, performance, and memory. This -should provide an error roughly equivalent to the quanization error of -using 16-bit fixed point representation. Generally, increasing over 32 -provides some accuracy benefits without a huge increase in -computational demands. - -*/ diff --git a/docs/doxygen/other/pmt.dox b/docs/doxygen/other/pmt.dox deleted file mode 100644 index 1bc6cbecd4..0000000000 --- a/docs/doxygen/other/pmt.dox +++ /dev/null @@ -1,506 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_pmt Polymorphic Types - -\section pmt_introduction Introduction - -Polymorphic Types are opaque data types that are designed as generic -containers of data that can be safely passed around between blocks and -threads in GNU Radio. They are heavily used in the stream tags and -message passing interfaces. The most complete list of PMT function is, -of course, the source code, specifically the header file pmt.h. This -manual page summarizes the most important features and points of PMTs. - -Let's dive straight into some Python code and see how we can use -PMTs: - -\code ->>> import pmt ->>> P = pmt.from_long(23) ->>> type(P) -<class 'pmt.pmt_swig.swig_int_ptr'> ->>> print P -23 ->>> P2 = pmt.from_complex(1j) ->>> type(P2) -<class 'pmt.pmt_swig.swig_int_ptr'> ->>> print P2 -0+1i ->>> pmt.is_complex(P2) -True -\endcode - -First, the pmt module is imported. We assign two values (P and P2) -with PMTs using the from_long() and from_complex() calls, -respectively. As we can see, they are both of the same type! This -means we can pass these variables to C++ through SWIG, and C++ can -handle this type accordingly. - -The same code as above in C++ would look like this: - -\code -#include <pmt/pmt.h> -// [...] -pmt::pmt_t P = pmt::from_long(23); -std::cout << P << std::endl; -pmt::pmt_t P2 = pmt::from_complex(gr_complex(0, 1)); // Alternatively: pmt::from_complex(0, 1) -std::cout << P2 << std::endl; -std::cout << pmt::is_complex(P2) << std::endl; -\endcode - -Two things stand out in both Python and C++. First, we can simply print -the contents of a PMT. How is this possible? Well, the PMTs have -in-built capability to cast their value to a string (this is not -possible with all types, though). Second, PMTs must obviously know -their type, so we can query that, e.g. by calling the is_complex() -method. - -When assigning a non-PMT value to a PMT, we can use the from_* -methods, and use the to_* methods to convert back: - -\code -pmt::pmt_t P_int = pmt::from_long(42); -int i = pmt::to_long(P_int); -pmt::pmt_t P_double = pmt::from_double(0.2); -double d = pmt::to_double(P_double); -\endcode - -String types play a bit of a special role in PMTs, as we will see -later, and have their own converter: - -\code -pmt::pmt_t P_str = pmt::string_to_symbol("spam"); -pmt::pmt_t P_str2 = pmt::intern("spam"); -std::string str = pmt::symbol_to_string(P_str); -\endcode - -The pmt::intern is another way of saying pmt::string_to_symbol. - -In Python, we can make use of the dynamic typing, and there's actually a -helper function to do these conversions (C++ also has a helper -function for converting to PMTs called pmt::mp(), but it's less -powerful, and not quite as useful, because types are always strictly -known in C++): - -\code -P_int = pmt.to_pmt(42) -i = pmt.to_python(P_int) -P_double = pmt.to_pmt(0.2) -d = pmt.to_double(P_double) -\endcode - -On a side note, there are three useful PMT constants, which can be -used in both Python and C++ domains. In C++, these can be used as -such: - -\code -pmt::pmt_t P_true = pmt::PMT_T; -pmt::pmt_t P_false = pmt::PMT_F; -pmt::pmt_t P_nil = pmt::PMT_NIL; -\endcode - -In Python: - -\code -P_true = pmt.PMT_T -P_false = pmt.PMT_F -P_nil = pmt.PMT_NIL -\endcode - -pmt.PMT_T and pmt.PMT_F are boolean PMT types. - -To be able to go back to C++ data types, we need to be able to find -out the type from a PMT. The family of is_* methods helps us do that: - -\code -double d; -if (pmt::is_integer(P)) { - d = (double) pmt::to_long(P); -} else if (pmt::is_real(P)) { - d = pmt::to_double(P); -} else { - // We really expected an integer or a double here, so we don't know what to do - throw std::runtime_error("expected an integer!"); -} -\endcode - -It is important to do type checking since we cannot unpack a PMT of -the wrong data type. - -We can compare PMTs without knowing their type by using the -pmt::equal() function: - -\code -if (pmt::eq(P_int, P_double)) { - std::cout << "Equal!" << std::endl; // This line will never be reached -\endcode - -The rest of this page provides more depth into how to handle different -data types with the PMT library. - - - -\section pmt_datatype PMT Data Type - -All PMTs are of the type pmt::pmt_t. This is an opaque container and -PMT functions must be used to manipulate and even do things like -compare PMTs. PMTs are also \a immutable (except PMT vectors). We -never change the data in a PMT; instead, we create a new PMT with the -new data. The main reason for this is thread safety. We can pass PMTs -as tags and messages between blocks and each receives its own copy -that we can read from. However, we can never write to this object, and -so if multiple blocks have a reference to the same PMT, there is no -possibility of thread-safety issues of one reading the PMT data while -another is writing the data. If a block is trying to write new data to -a PMT, it actually creates a new PMT to put the data into. Thus we -allow easy access to data in the PMT format without worrying about -mutex locking and unlocking while manipulating them. - -PMTs can represent the following: - -- Boolean values of true/false -- Strings (as symbols) -- Integers (long and uint64) -- Floats (as doubles) -- Complex (as two doubles) -- Pairs -- Tuples -- Vectors (of PMTs) -- Uniform vectors (of any standard data type) -- Dictionaries (list of key:value pairs) -- Any (contains a boost::any pointer to hold anything) - -The PMT library also defines a set of functions that operate directly -on PMTs such as: - -- Equal/equivalence between PMTs -- Length (of a tuple or vector) -- Map (apply a function to all elements in the PMT) -- Reverse -- Get a PMT at a position in a list -- Serialize and deserialize -- Printing - -The constants in the PMT library are: - -- pmt::PMT_T - a PMT True -- pmt::PMT_F - a PMT False -- pmt::PMT_NIL - an empty PMT (think Python's 'None') - -\section pmt_insert Inserting and Extracting Data - -Use pmt.h for a complete guide to the list of functions used to create -PMTs and get the data from a PMT. When using these functions, remember -that while PMTs are opaque and designed to hold any data, the data -underneath is still a C++ typed object, and so the right type of -set/get function must be used for the data type. - -Typically, a PMT object can be made from a scalar item using a call -like "pmt::from_<type>". Similarly, when getting data out of a -PMT, we use a call like "pmt::to_<type>". For example: - -\code -double a = 1.2345; -pmt::pmt_t pmt_a = pmt::from_double(a); -double b = pmt::to_double(pmt_a); - -int c = 12345; -pmt::pmt_t pmt_c = pmt::from_long(c); -int d = pmt::to_long(pmt_c); -\endcode - -As a side-note, making a PMT from a complex number is not obvious: - -\code -std::complex<double> a(1.2, 3.4); -pmt::pmt_t pmt_a = pmt::make_rectangular(a.real(), b.imag()); -std::complex<double> b = pmt::to_complex(pmt_a); -\endcode - -Pairs, dictionaries, and vectors have different constructors and ways -to manipulate them, and these are explained in their own sections. - - -\section pmt_strings Strings - -PMTs have a way of representing short strings. These strings are -actually stored as interned symbols in a hash table, so in other -words, only one PMT object for a given string exists. If creating a -new symbol from a string, if that string already exists in the hash -table, the constructor will return a reference to the existing PMT. - -We create strings with the following functions, where the second -function, pmt::intern, is simply an alias of the first. - -\code -pmt::pmt_t str0 = pmt::string_to_symbol(std::string("some string")); -pmt::pmt_t str1 = pmt::intern(std::string("some string")); -\endcode - -The string can be retrieved using the inverse function: - -\code -std::string s = pmt::symbol_to_string(str0); -\endcode - - -\section pmt_tests Tests and Comparisons - -The PMT library comes with a number of functions to test and compare -PMT objects. In general, for any PMT data type, there is an equivalent -"pmt::is_<type>". We can use these to test the PMT before trying -to access the data inside. Expanding our examples above, we have: - -\code -pmt::pmt_t str0 = pmt::string_to_symbol(std::string("some string")); -if(pmt::is_symbol(str0)) - std::string s = pmt::symbol_to_string(str0); - -double a = 1.2345; -pmt::pmt_t pmt_a = pmt::from_double(a); -if(pmt::is_double(pmt_a)) - double b = pmt::to_double(pmt_a); - -int c = 12345; -pmt::pmt_t pmt_c = pmt::from_long(c); -if(pmt::is_long(pmt_a)) - int d = pmt::to_long(pmt_c); - -\\ This will fail the test. Otherwise, trying to coerce \b pmt_c as a -\\ double when internally it is a long will result in an exception. -if(pmt::is_double(pmt_a)) - double d = pmt::to_double(pmt_c); - -\endcode - - -\section pmt_dict Dictionaries - -PMT dictionaries are lists of key:value pairs. They have a -well-defined interface for creating, adding, removing, and accessing -items in the dictionary. Note that every operation that changes the -dictionary both takes a PMT dictionary as an argument and returns a -PMT dictionary. The dictionary used as an input is not changed and the -returned dictionary is a new PMT with the changes made there. - -The following is a list of PMT dictionary functions. Click through to -get more information on what each does. - -- bool pmt::is_dict(const pmt_t &obj) -- pmt_t pmt::make_dict() -- pmt_t pmt::dict_add(const pmt_t &dict, const pmt_t &key, const pmt_t &value) -- pmt_t pmt::dict_delete(const pmt_t &dict, const pmt_t &key) -- bool pmt::dict_has_key(const pmt_t &dict, const pmt_t &key) -- pmt_t pmt::dict_ref(const pmt_t &dict, const pmt_t &key, const pmt_t ¬_found) -- pmt_t pmt::dict_items(pmt_t dict) -- pmt_t pmt::dict_keys(pmt_t dict) -- pmt_t pmt::dict_values(pmt_t dict) - -This example does some basic manipulations of PMT dictionaries in -Python. Notice that we pass the dictionary \a a and return the results -to \a a. This still creates a new dictionary and removes the local -reference to the old dictionary. This just keeps our number of -variables small. - -\code -import pmt - -key0 = pmt.intern("int") -val0 = pmt.from_long(123) -val1 = pmt.from_long(234) - -key1 = pmt.intern("double") -val2 = pmt.from_double(5.4321) - -# Make an empty dictionary -a = pmt.make_dict() - -# Add a key:value pair to the dictionary -a = pmt.dict_add(a, key0, val0) -print a - -# Add a new value to the same key; -# new dict will still have one item with new value -a = pmt.dict_add(a, key0, val1) -print a - -# Add a new key:value pair -a = pmt.dict_add(a, key1, val2) -print a - -# Test if we have a key, then delete it -print pmt.dict_has_key(a, key1) -a = pmt.dict_delete(a, key1) -print pmt.dict_has_key(a, key1) - -ref = pmt.dict_ref(a, key0, pmt.PMT_NIL) -print ref - -# The following should never print -if(pmt.dict_has_key(a, key0) and pmt.eq(ref, pmt.PMT_NIL)): - print "Trouble! We have key0, but it returned PMT_NIL" -\endcode - -\section pmt_vectors Vectors - -PMT vectors come in two forms: vectors of PMTs and vectors of uniform -data. The standard PMT vector is a vector of PMTs, and each PMT can be -of any internal type. On the other hand, uniform PMTs are of a -specific data type which come in the form: - -- (u)int8 -- (u)int16 -- (u)int32 -- (u)int64 -- float32 -- float64 -- complex 32 (std::complex<float>) -- complex 64 (std::complex<double>) - -That is, the standard sizes of integers, floats, and complex types of -both signed and unsigned. - -Vectors have a well-defined interface that allows us to make, set, -get, and fill them. We can also get the length of a vector with -pmt::length. - -For standard vectors, these functions look like: - -- bool pmt::is_vector(pmt_t x) -- pmt_t pmt::make_vector(size_t k, pmt_t fill) -- pmt_t pmt::vector_ref(pmt_t vector, size_t k) -- void pmt::vector_set(pmt_t vector, size_t k, pmt_t obj) -- void pmt::vector_fill(pmt_t vector, pmt_t fill) - -Uniform vectors have the same types of functions, but they are data -type-dependent. The following list tries to explain them where you -substitute the specific data type prefix for \a dtype (prefixes being: -u8, u16, u32, u64, s8, s16, s32, s64, f32, f64, c32, c64). - -- bool pmt::is_(dtype)vector(pmt_t x) -- pmt_t pmt::make_(dtype)vector(size_t k, (dtype) fill) -- pmt_t pmt::init_(dtype)vector(size_t k, const (dtype*) data) -- pmt_t pmt::init_(dtype)vector(size_t k, const std::vector<dtype> data) -- pmt_t pmt::(dtype)vector_ref(pmt_t vector, size_t k) -- void pmt::(dtype)vector_set(pmt_t vector, size_t k, (dtype) x) -- const dtype* pmt::(dtype)vector_elements(pmt_t vector, size_t &len) -- dtype* pmt::(dtype)vector_writable_elements(pmt_t vector, size_t &len) - -\b Note: We break the contract with vectors. The 'set' functions -actually change the data underneath. It is important to keep track of -the implications of setting a new value as well as accessing the -'vector_writable_elements' data. Since these are mostly standard data -types, sets and gets are atomic, so it is unlikely to cause a great -deal of harm. But it's only unlikely, not impossible. Best to use -mutexes whenever manipulating data in a vector. - - -\subsection pmt_blob BLOB - -A BLOB is a 'binary large object' type. In PMT's, this is actually -just a thin wrapper around a u8vector. - -\section pmt_pairs Pairs - -Pairs are inspired by LISP 'cons' data types, so you will find the -language here comes from LISP. A pair is just a pair of PMT -objects. They are manipulated using the following functions: - -- bool pmt::is_pair(const pmt_t &obj): Return true if obj is a pair, else false -- pmt_t pmt::cons(const pmt_t &x, const pmt_t &y): construct new pair -- pmt_t pmt::car(const pmt_t &pair): get the car of the pair (first object) -- pmt_t pmt::cdr(const pmt_t &pair): get the cdr of the pair (second object) -- void pmt::set_car(pmt_t pair, pmt_t value): Stores value in the car field -- void pmt::set_cdr(pmt_t pair, pmt_t value): Stores value in the cdr field - - -\section pmt_serdes Serializing and Deserializing - -It is often important to hide the fact that we are working with PMTs -to make them easier to transmit, store, write to file, etc. The PMT -library has methods to serialize data into a string buffer or a -string and then methods to deserialize the string buffer or string -back into a PMT. We use this extensively in the metadata files (see -\ref page_metadata). - -- bool pmt::serialize(pmt_t obj, std::streambuf &sink) -- std::string pmt::serialize_str(pmt_t obj) -- pmt_t pmt::deserialize(std::streambuf &source) -- pmt_t pmt::deserialize_str(std::string str) - -For example, we will serialize the data above to make it into a string -ready to be written to a file and then deserialize it back to its -original PMT. - -\code -import pmt - -key0 = pmt.intern("int") -val0 = pmt.from_long(123) - -key1 = pmt.intern("double") -val1 = pmt.from_double(5.4321) - -# Make an empty dictionary -a = pmt.make_dict() - -# Add a key:value pair to the dictionary -a = pmt.dict_add(a, key0, val0) -a = pmt.dict_add(a, key1, val1) - -print a - -ser_str = pmt.serialize_str(a) -print ser_str - -b = pmt.deserialize_str(ser_str) -print b - -\endcode - -The line where we 'print ser_str' will print and parts will be -readable, but the point of serializing is not to make a human-readable -string. This is only done here as a test. - - -\section pmt_printing Printing - -In Python, the __repr__ function of a PMT object is overloaded to call -'pmt::write_string'. This means that any time we call a formatted -printing operation on a PMT object, the PMT library will properly -format the object for display. - -In C++, we can use the 'pmt::print(object)' function or print the -contents is using the overloaded "<<" operator with a stream buffer -object. In C++, we can inline print the contents of a PMT like: - -\code -pmt::pmt_t a pmt::from_double(1.0); -std::cout << "The PMT a contains " << a << std::endl; -\endcode - - -\section pmt_python Conversion between Python Objects and PMTs - -Although PMTs can be manipulated in Python using the Python versions -of the C++ interfaces, there are some additional goodies that make it -easier to work with PMTs in python. There are functions to automate -the conversion between PMTs and Python types for booleans, strings, -integers, longs, floats, complex numbers, dictionaries, lists, tuples -and combinations thereof. - -Two functions capture most of this functionality: - -\code -pmt.to_pmt # Converts a python object to a PMT. -pmt.to_python # Converts a PMT into a python object. -\endcode - -*/ diff --git a/docs/doxygen/other/prefs.dox b/docs/doxygen/other/prefs.dox deleted file mode 100644 index d8c2782b1d..0000000000 --- a/docs/doxygen/other/prefs.dox +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_prefs Configuration files - -\section prefs Configuration / Preference Files - -GNU Radio defines some of its basic behavior through a set of -configuration files located in -${prefix}/etc/gnuradio/conf.d. Different components have different -files listed in here for the various properties. These will be read -once when starting a GNU Radio application, so updates during runtime -will not affect them. - -The configuration files use the following format: - -\code -# Stuff from section 1 -[section1] -var1 = value1 -var2 = value2 # value of 2 - -# Stuff from section 2 -[section2] -var3 = value3 -\endcode - -In this file, the hash mark ('#') indicates a comment and blank lines -are ignored. Section labels are defined inside square brackets as a -group distinguisher. All options must be associated with a section -name. The options are listed one per line with the option name is -given followed by an equals ('=') sign and then the value. - -All section and option names must not have white spaces. If a value -must have white space, the it MUST be put inside quotes. Any quoted -value will have its white space preserved and the quotes internally -will be stripped. As an example, on Apple desktops, an output device -of "Display Audio" is a possible output device and can be set as: - -\code -[audio_osx] -default_output_device = "Display Audio" -\endcode - -The result will pass Display Audio to the audio setup. - -The value of an option can be a string or number and retrieved through -a few different interfaces. There is a single preference object -created when GNU Radio is launched. In Python, you can get this by -making a new variable: - -\code -p = gr.prefs() -\endcode - -Similarly, in C++, we get a reference to the object by explicitly -calling for the singleton of the object: - -\code - prefs *p = prefs::singleton(); -\endcode - -The methods associated with this preferences object are (from class gr::prefs): - -\code - bool has_section(string section) - bool has_option(string section, string option) - string get_string(string section, string option, string default_val) - bool get_bool(string section, string option, bool default_val) - long get_long(string section, string option, long default_val) - double get_double(string section, string option, double default_val) -\endcode - -When setting a Boolean value, we can use 0, 1, "True", "true", -"False", "false", "On", "on", "Off", and "off". - -All configuration preferences in these files can also be overloaded by -an environmental variable. The environmental variable is named based -on the section and option name from the configuration file as: - -\code - GR_CONF_<SECTION>_<OPTION> = <value> -\endcode - -The "GR_CONF_" is a prefix to identify this as a GNU Radio -configuration variable and the section and option names are in -uppercase. The value is the same format that would be used in the -config file itself. - -*/ diff --git a/docs/doxygen/other/python_blocks.dox b/docs/doxygen/other/python_blocks.dox deleted file mode 100644 index 2381b0cad0..0000000000 --- a/docs/doxygen/other/python_blocks.dox +++ /dev/null @@ -1,163 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_python_blocks Python Blocks - -How to create blocks in Python - -\section pyblocks_streaming Streaming Data Blocks - -We create blocks in Python very much like we would in C++, just with -more Python. Figure out which type of block you want to create: - -\li general block (gr.basic_block) -\li synchronous block (gr.sync_block) -\li decimator (gr.sync_decimator) -\li interpolator (gr.sync_interpolator) - -The block class inherits from one of these base classes, and then in -defining the parent class, we set the I/O signature. However, unlike -in C++ where we use the gr::io_signature class, here we can just -create a Python list of the I/O data sizes using numpy data types: - -\li numpy.int8 -\li numpy.int16 -\li numpy.int32 -\li numpy.float32 -\li numpy.float64 - -Like a normal C++ version of the block, we then create and initialize -any variables in the constructor, define any setters and getters, and -create the work function. The prototype for the work function is quite -simple: - -\code -def work(self, input_items, output_items) -\endcode - -The input_items and output_items are lists of lists. The input_items -contains a vector of input samples for every input stream, and the -output_items is a vector for each output stream where we can place -items. Then length of output_items[0] is equivalent to the -noutput_items concept we are so familiar with from the C++ blocks. - -Following is an example Python block that adds two input streams -together. This block is used in the qa_block_gateway.py test code. - -\code -class add_2_f32_1_f32(gr.sync_block): - def __init__(self): - gr.sync_block.__init__( - self, - name = "add 2 f32", - in_sig = [numpy.float32, numpy.float32], - out_sig = [numpy.float32], - ) - - def work(self, input_items, output_items): - output_items[0][:] = input_items[0] + input_items[1] - return len(output_items[0]) -\endcode - -The block defines two input floating point streams by setting in_sig -to "[numpy.float32, numpy.float32]" and a single output float stream -in out_sig of "[numpy.float32]." - -The work function then just adds the two input streams together. The -streams are input_items[0] and input_items[1]. The block still returns -the concept of noutput_items like we use in C++, only we get it here -by getting len(output_items[0]). Because this is a sync_block, we also -know that the size of the input_items for both streams is the same as -the size of the output_items vector. - - - -\section pyblocks_tags Using Stream Tags - -Python blocks have access to the stream tag system like their C++ -counterparts. The interface is almost identical except they behave -just a bit more like we would expect in Python. - -To add tags to the data stream, we use the add_item_tag function: - -\code -def work(self, input_items, output_items): - .... - add_item_tag(which_output, abs_offset, - key, value, srcid) - .... -\endcode - -The abs_offset is an integer of the sample that the tag is attached -to, and key and value are both PMTs to set the key:value pair of the -tag information, and the srcid is an optional PMT to define the source -of the block that generate the tag. - -We then can get tags using either the get_tags_in_range or -get_tags_in_window. Again, like their C++ counter parts, the -get_tags_in_range uses the absolute item offset numbering (using -nitems_read) while the get_tags_in_window uses relative offsets within -the current window of items available to the work function. The main -difference from the C++ function is that instead of having the first -argument be a vector where the tags are stored, the Python version -just returns a list of tags. We would use it like this: - -\code -def work(self, input_items, output_items): - .... - tags = get_tags_in_window(which_input, rel_start, rel_end) - .... -\endcode - - - -\section pyblocks_msgs Using Message Passing - -Again, like their C++ counterparts, Python blocks can use the -asynchronous message passing interface. We define output message -handlers using: - -\code -self.message_port_register_out(pmt.intern("<port name>")) -\endcode - -We can then post messages to this using the message_port_pub function: - -\code -self.message_port_pub(pmt.intern("<port name>"), <pmt message>) -\endcode - -We then register input messages and handlers in similar ways: - -\code -self.message_port_register_in(pmt.intern("<port name>")) -self.set_msg_handler(pmt.intern("<port name>"), <msg handler function>) -\endcode - -Putting this together below is a very simple example: - -\code -class msg_block(gr.basic_block): - def __init__(self): - gr.basic_block.__init__( - self, - name="msg_block", - in_sig=None, - out_sig=None) - - self.message_port_register_out(pmt.intern('msg_out')) - self.message_port_register_in(pmt.intern('msg_in')) - self.set_msg_handler(pmt.intern('msg_in'), self.handle_msg) - - def handle_msg(self, msg): - self.message_port_pub(pmt.intern('msg_out'), - pmt.intern('message received!')) -\endcode - -*/ diff --git a/docs/doxygen/other/stream_tags.dox b/docs/doxygen/other/stream_tags.dox deleted file mode 100644 index 146218796e..0000000000 --- a/docs/doxygen/other/stream_tags.dox +++ /dev/null @@ -1,245 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_stream_tags Stream Tags - -\section stream_tags_introduction Introduction - -GNU Radio was originally a streaming system with no other mechanism to -pass data between blocks. Streams of data are a model that work well -for samples, bits, etc., but can lack for control and meta data. - -Part of this is solved using the existing message passing interface, which -allows blocks to subscribe to messages published by any other block in -the flowgraph (see \ref page_msg_passing). The main drawback to the -message passing system is that is works asynchronously, meaning that -there is no guarantee when a message may arrive relative to the data -stream. - -Stream tags are an isosynchronous data stream that runs parallel to -the main data stream. A stream \a tag is generated by a block's work -function and from there on flows downstream alongside a particular sample, -until it reaches a sink or is forced to stop propagating by another -block. - -Stream tags are defined for a specific item in the data stream and are -formed as a key:value pair. The \a key identifies what the \a value represents -while the value holds the data that the tag contains. Both \a key and -\a value are PMTs (\ref page_pmt) where the \a key is a PMT symbol while -the \a value is any type of PMT and can therefore handle any data we wish -to pass. An additional part of the tag is the \a srcid, which is a PMT -symbol and is used to identify the block that created the tag (which -is usually the block's alias). - - -\section stream_tags_block_api_extensions API Extensions to the gr::block - -To enable the stream tags, we have extended the API of gr::block to -understand \a absolute item numbers. In the data stream model, each -block's work function is given a buffer in the data stream that is -referenced from 0 to N-1. This is a \a relative offset into the data -stream. The absolute reference starts from the beginning of the -flowgraph and continues to count up with every item. Each input stream -is associated with a concept of the 'number of items read' and each -output stream has a 'number of items written'. These are retrieved during -runtime using the two API calls: - -\code -unsigned long int nitems_read(unsigned int which_input); -unsigned long int nitems_written(unsigned int which_output); -\endcode - -Each tag is associated with some item in this absolute time scale that -is calculated using these functions. - -Like the rest of the data stream, the number of items read/written are -only updated once during the call to work. So in a work function, -nitems_read/written will refer to the state of the data stream at the -start of the work function. We must therefore add to this value the -current relative offset in the data stream. So if we are iterating \a -i over all output items, we would write the stream tag to output ports -at <em>nitems_written(0)+i</em> for the 0th output port. - - -\section stream_tags_api Stream Tags API - -The stream tags API is split into two parts: adding tags to a stream, -and getting tags from a stream. -Note that the functions described below are only meant to be accessed -within a call to general_work/work. While they can be called at other points -in time by a block, the behavior outside of work is undefined without -exact knowledge of the item counts in the buffers. - - -\subsection stream_tags_add_item_tag Adding a Tag to a Stream - -We add a tag to a particular output stream of the block using: - -\li gr::block::add_item_tag: Adds an item tag to a particular output port -using a gr::tag_t data type or by specifying the tag values. - -We can output them to multiple output streams if we want, but to do so -means calling this function once for each port. This function can be -provided with a gr::tag_t data type, or each value of the tag can be -explicitly given. - -Again, a tag is defined as: - -\li offset: The offset, in absolute item time, of the tag in the data -stream. -\li key: the PMT symbol identifying the type of tag. -\li value: the PMT holding the data of the tag. -\li srcid: (optional) the PMT symbol identifying the block which -created the tag. - -We can create a gr::tag_t structure to hold all of the above -information of a tag, which is probably the easiest/best way to do -it. The gr::tag_t struct is defined as having the same members as in -the above list. To add a gr::tag_t tag to a stream, use the function: - -\code - void add_item_tag(unsigned int which_output, const tag_t &tag); -\endcode - -The secondary API allows us to create a tag by explicitly listing all -of the tag information in the function call: - -\code - void add_item_tag(unsigned int which_output, - uint64_t abs_offset, - const pmt::pmt_t &key, - const pmt::pmt_t &value, - const pmt::pmt_t &srcid=pmt::PMT_F); -\endcode - - -\subsection stream_tags_get_item_tags Getting tags from a Stream - -To get tags from a particular input stream, we have two -functions we can use: - -\li gr::block::get_tags_in_range: Gets all tags from a particular -input port between a certain range of items (in absolute item time). - -\li gr::block::get_tags_in_window: Gets all tags from a particular -input port between a certain range of items (in relative item time -within the work function). - -The difference between these functions is working in absolute item -time versus relative item time. Both of these pass back vectors of -gr::tag_t, and they both allow -specifying a particular key (as a PMT symbol) to filter against -(or the fifth argument can be left out to search for all keys). -Filtering for a certain key reduces the effort inside the work function -for getting the right tag's data. - -For example, this call just returns any tags between the given range of items: - -\code - void get_tags_in_range(std::vector<tag_t> &v, - unsigned int which_input, - uint64_t abs_start, - uint64_t abs_end); -\endcode - -Adding a fifth argument to this function allows us to filter on the -key \a key. - -\code - void get_tags_in_range(std::vector<tag_t> &v, - unsigned int which_input, - uint64_t abs_start, - uint64_t abs_end, - const pmt::pmt_t &key); -\endcode - - -\section stream_tags_propagation Tag Propagation - -Tags are propagated downstream from block to block like the normal -data streams. How tags are actually moved depends on a specific -propagation policy. We defined three types of policies: - -\li All-to-All: all tags from any input port are replicated to all -output ports -\li One-to-One: tags from input port \a i are only copied to output -port \a i (depends on num inputs = num outputs). -\li Dont: Does not propagate tags. Tags are either stopped here or the -work function recreates them in some manner. - -The default behavior of a block is the 'All-to-All' method of -propagation. - -To set a different propagation policy, use the function: - -\code - void set_tag_propagation_policy(tag_propagation_policy_t p); -\endcode - -See the gr::block::tag_propagation_policy_t documentation for details -on this enum type. - - -\subsection stream_tags_rate_changes Tag Propagation through Rate Changes - -When a tag is propagated through a block that has a rate change, the -item's offset in the data stream will change. The scheduler uses the -block's gr::block::relative_rate concept to perform the update on the -tag's offset value. The relative rate of a block determines the -relationship between the input rate and output rate. Decimators that -decimate by a factor of \a D have a relative rate of <em>1/D</em>. - -Synchronous blocks (gr::sync_block), decimators (gr::sync_decimator), -and interpolators (gr::sync_interpolator) all have pre-defined and -well-understood relative rates. A standard gr::block has a default -relative rate of 1.0, but this must be set if it does not work this -way. Often, we use a gr::block because we have no pre-conceived notion -of the number of input to output items. If it is important to pass -tags through these blocks that respect the change in item value, we -would have to use the TPP_DONT tag propagation policy and handle the -propagation internally. - -In no case is the value of the tag modified when propagating through a -block. This becomes relevant when using \ref page_tagged_stream_blocks. - -\section stream_tags_issues Notes on How to Use Tags - -Tags can be very useful to an application, and their use is -spreading. USRP sources generate tag information on the time, sample -rate, and frequency of the board if anything changes. We have a meta -data file source/sink (see \ref page_metadata) that use tags to store -information about the data stream. But there are things to think about -when using tags in a block. - -First, when tags are not being used, there is almost no effect on the -scheduler. However, when we use tags, we add overhead by getting and -extracting tags from a data stream. We also use overhead in -propagating the tags. For each tag, each block must copy a vector of -tags from the output port(s) of one block to the input port(s) of the -next block(s). These copy operations can add up. - -The key is to minimize the use of tags. Use them when and only when -necessary and try to provide some control over how tags are generated -to control their frequency. A good example is the USRP source, which -generates a time tag. If it generated a tag with every sample, we -would have thousands of tags per second, which would add a significant -amount of overhead. This is because if we started at time <em>t0</em> at -sample rate <em>sr</em>, then after <em>N</em> samples, we know that -we are now at time <em>t0 + N/sr</em>. So continuously producing new -tags adds no information. - -The main issue we need to deal with in the above situation is when -there is a discontinuity in the packets received from the USRP. Since -we have no way of knowing in the flowgraph how many samples were -potentially lost, we have lost track of the timing information. The -USRP driver recognizes when packets have been dropped and uses this to -queue another tag, which allows us to resync. Likewise, any point the -sample rate or frequency changes, a new tag is issued. - -*/ diff --git a/docs/doxygen/other/tagged_stream_blocks.dox b/docs/doxygen/other/tagged_stream_blocks.dox deleted file mode 100644 index 1a10483323..0000000000 --- a/docs/doxygen/other/tagged_stream_blocks.dox +++ /dev/null @@ -1,320 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_tagged_stream_blocks Tagged Stream Blocks - -\section tsb_introduction Introduction - -A tagged stream block is a block that works on streamed, but packetized input data. -Think of packet data transmission: A data packet consists of N bytes. However, in -traditional GNU Radio blocks, if we stream N bytes into a block, there's no way of -knowing the packet boundary. This might be relevant: Perhaps the modulator has to -prepend a synchronisation word before every packet, or append a CRC. So while some -blocks don't care about packet boundaries, other blocks do: These are <em>tagged -stream blocks</em>. - -These blocks are different from all the other GNU Radio block types (gr::block, -gr::sync_block etc.) in that they are driven by the input: The PDU length tag tells the block how to -operate, whereas other blocks are output-driven (the scheduler tries to fill up the output -buffer are much as possible). - -\subsection tsb_howtheywork How do they work? - -As the name implies, tagged stream blocks use tags to identify PDU boundaries. -On the first item of a streamed PDU, there \em must be a tag with a specific -key, which stores the length of the PDU as a PMT integer. If anything else, or no tag, -is on this first item, this will cause the flow graph to crash! - -The scheduler then takes care of everything. When the work function is called, it -is guaranteed to contain exactly one complete PDU and enough space in the output buffer for -the output. - -\subsection tsb_relatestootherblocks How do they relate to other block types (e.g. sync blocks)? - -Tagged stream blocks and sync blocks are really orthogonal concepts, and a block could be -both (gr::digital::ofdm_frame_equalizer_vcvc is such a block). However, because the work -function is defined differently in these block types, there is no way to derive a block -from both gr::tagged_stream_block and gr::sync_block. - -If a block needs the tagged stream mechanism (i.e. knowing about PDU boundaries), it must be -derived from gr::tagged_stream_block. If it's also a sync block, it is still possible to -set gr::block::set_relative_rate(1.0) and/or the fixed rate functions. - -The way gr::tagged_stream_block works, it is still beneficial to specify a relative rate, -if possible. - - -\section tsb_creating Creating a tagged stream block - -To create a tagged stream block, the block must be derived from gr::tagged_stream_block. -Here's a minimal example of how the header file could look: -\code -#include <gnuradio/digital/api.h> -#include <gnuradio/tagged_stream_block.h> - -namespace gr { - namespace digital { - - class DIGITAL_API crc32_bb : virtual public tagged_stream_block - { - public: - typedef boost::shared_ptr<crc32_bb> sptr; - - static sptr make(bool check=false, const std::string& len_tag_key="packet_len"); - }; - - } // namespace digital -} // namespace gr - -\endcode - -It is very similar to any other block definition. Two things are stand out: First, -gnuradio/tagged_stream_block.h is included to allow deriving from gr::tagged_stream_block. - -The other thing is in the make function: the second argument is a string containing -the key of the length tags. This is not necessary (the block could get this information -hard-coded), but the usual way is to allow the user to change this tag, but give a -default value (in this case, \p packet_len). - -The implementation header (*_impl.h) also looks a bit different (again this is cropped to the relevant parts): -\code -#include <digital/crc32_bb.h> -namespace gr { - namespace digital { - - class crc32_bb_impl : public crc32_bb - { - public: - crc32_bb_impl(bool check, const std::string& len_tag_key); - ~crc32_bb_impl(); - - int calculate_output_stream_length(const gr_vector_int &ninput_items); - int work(int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items); - }; - - } // namespace digital -} // namespace gr -\endcode - -First, the \p work function signature is new. The argument list looks like that from -gr::block::general_work() (note: the arguments mean the same, too), but it's called -\p work like with the other derived block types (such as gr::sync_block). Also, there's a new -function: \p calculate_output_stream_length() is, in a sense, the opposite function to -gr::block::forecast(). Given a number of input items, it calculates the required number of -output items. Note how this relates to the fact that these blocks are input-driven. - -These two overrides (\p work() and \p calculate_output_stream_length() ) are what you need -for most tagged stream blocks. There are cases when you don't need to override the latter -because the default behaviour is enough, and other cases where you have to override more -than these two functions. These are discussed in \ref tsb_adv_usage. - -Finally, this is part of the actual block implementation (heavily cropped again, to highlight the relevant parts): -\code -#include <gnuradio/io_signature.h> -#include "crc32_bb_impl.h" - -namespace gr { - namespace digital { - - crc32_bb::sptr crc32_bb::make(bool check, const std::string& len_tag_key) - { - return gnuradio::get_initial_sptr (new crc32_bb_impl(check, len_tag_key)); - } - - crc32_bb_impl::crc32_bb_impl(bool check, const std::string& len_tag_key) - : tagged_stream_block("crc32_bb", - io_signature::make(2, 1, sizeof (char)), - io_signature::make(1, 1, sizeof (char)), - len_tag_key), - d_check(check) - { - } - - int - crc32_bb_impl::calculate_output_stream_length(const gr_vector_int &ninput_items) - { - if (d_check) { - return ninput_items[0] - 4; - } else { - return ninput_items[0] + 4; - } - } - - int - crc32_bb_impl::work (int noutput_items, - gr_vector_int &ninput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) - { - const unsigned char *in = (const unsigned char *) input_items[0]; - unsigned char *out = (unsigned char *) output_items[0]; - - // Do all the signal processing... - // Don't call consume! - - return new_packet_length; - } - - } // namespace digital -} // namespace gr -\endcode - -The make function is not different to any other block. The constructor calls -gr::tagged_stream_block::tagged_stream_block() as expected, but note that it passes the -key of the length tag to the parent constructor. - -The block in question is a CRC block, and it has two modes: It can check the CRC (which is -then removed), or it can append a CRC to a sequence of bytes. The \p calculate_output_stream_length() function -is thus very simple: depending on how the block is configured, the output is either 4 bytes -longer or shorter than the input stream. - -The \p work() function looks very similar to any other work function. When writing the -signal processing code, the following things must be kept in mind: -- The work function is called for exactly one PDU, and no more (or less) may be processed -- \p ninput_items contains the exact number of items in this PDU (at every port). - These items \em will be consumed after \p work() exits. -- Don't call \p consume() or \p consume_each() yourself! gr::tagged_stream_block will do that - for you. -- You can call \p produce() or \p produce_each(), if you're doing something complicated. - Don't forget to return WORK_CALLED_PRODUCE in that case. - - -\subsection tsb_note_on_tp A note on tag propagation - -Despite using tags for a special purpose, all tags that are not the length tag are treated -exactly as before: use gr::block::set_tag_propagation_policy() in the constructor. - -In a lot of the cases, though, you will need to specify set_tag_propagation_policy(TPP_DONT) -and manually handle the tag propagation in \p work(). This is because the unknown length of -the PDUs at compile time prohibits us from setting a precise relative rate of the block, -which is a requirement for automatic tag propagation. -Only if the tagged stream block is also a sync block (including interpolators and decimators, -i.e. blocks with an integer rate change), can automatic tag propagation be reliably used. - -It is a general rule of GNU Radio blocks that they "properly" propagate tags, whatever this -means for a specific application. - -The CRC block seems to a very simple block, but it's already complicated enough to confuse -the automatic tag propagation. For example, what happens to tags which are on the CRC? Do -they get removed, or do they get moved to the last item before the CRC? Also, the input to -output rate is different for every PDU length. - -In this case, it is necessary for the developer to define a tag propagation policy and -implement it in \p work(). Also, it is good practice to specify that tag propagation policy -in the blocks documentation. - -The actual length tags \em are treated differently, though. Most importantly, you don't have -to write the new length tag yourself. The key for the output length tag is the same as that -on the input, if you don't want this, you must override gr::tagged_stream_block::update_length_tags(). - - -\section tsb_connecting Connecting regular streaming blocks and tagged stream blocks - -From the scheduler's point of view, all blocks are equivalent, and as long as the I/O -signatures are compatible, all of these blocks can be connected. - -However, it is important to note that tagged stream blocks expect correctly tagged -streams, i.e. a length tag with a number of items at the beginning of every packet. -If this is not the case, the <em>flow graph will crash</em>. -The most common cases are discussed separately: - -Connecting a tagged stream block to a regular stream block: This is never a problem, -since regular blocks don't care about the values of the tags. - -Connecting regular stream blocks to tagged stream blocks: This will usually not work. -One solution is to use the gr::blocks:stream_to_tagged_stream adapter block. It will -periodically add tags at regular intervals, making the input valid for the tagged stream -block. Make sure to directly connect this block to the tagged stream block, or the -packet size might be changed to a different value from the tag value. - -Mixing block types: This is possible if none of the regular stream blocks change -the rate. The ofdm_tx and ofdm_rx hierarchical blocks do this. - - -\section tsb_adv_usage Advanced Usage - -It is generally recommended to read the block documentation of gr::tagged_stream_block. - -A few special cases are described here: - -\subsection tsb_multiplelentags Multiple length tags - -In some cases, a single tag is not enough. One example is the OFDM receiver: one OFDM frame contains a certain number of OFDM symbols, and another number of bytes--these numbers are only -very loosely related, and one cannot be calculated from the other. - -gr::digital::ofdm_serializer_vcc is such a block. It is driven by the number of OFDM frames, -but the output is determined by the number of complex symbols. In order to use multiple length -tag keys, it overrides gr::tagged_stream_block::update_length_tags(). - -\subsection tsb_backtogrblock Falling back to gr::block behaviour - -If, at compile-time, it is uncertain whether or not a block should be a -gr::tagged_stream_block, there is the possibility of falling back to gr::block behaviour. - -To do this, simple don't pass an empty string as length tag key. Instead of crashing, -a tagged stream block will behave like a gr::block. - -This has some consequences: The work function must have all the elements of a -gr::block::general_work() function, including calls to consume(). Because such a block must -allow both modes of operation (PDUs with tags, and infinite-stream), the work function -must check which mode is currently relevant. Checking if -gr::tagged_stream_block::d_length_tag_key_str is empty is a good choice. - -gr::digital::ofdm_cyclic_prefixer implements this. - -\subsection tsb_otherwaysdetermineninput Other ways to determine the number of input items - -If the number of input items is not stored as a pmt::pmt_integer, but there is a way to determine -it, gr::tagged_stream_block::parse_length_tags() can be overridden to figure out the length -of the PDU. - -\section tsb_examples Examples - -\subsection tsb_CRC32 CRC32 - -Block: gr::digital::crc32_bb - -This is a very simple block, and a good example to start with. - -\subsection tsb_ofdmeq OFDM Frame Equalizer - -Block: gr::digital::ofdm_frame_equalizer_vcvc - -This block would be a sync block if tagged stream blocks didn't exist. It also uses more -than one tag to determine the output. - -\subsection tsb_muxer Tagged Stream Muxer - -Block: gr::blocks::tagged_stream_mux - -Use this to multiplex any number of tagged streams. - -\subsection tsb_ofdmprefixer Cyclic Prefixer (OFDM) - -Block: gr::digital::ofdm_cyclic_prefixer - -This block uses the gr::block behaviour fallback. - -\section tsb_troubleshooting Troubleshooting - -<b>My flow graph crashes with the error message "Missing length tag".</b> - -This means the input of a tagged stream block was not correctly tagged. -The most common cause is when connecting a regular streaming block to a tagged -stream block. You can check the log output for the item number and port where -this happened. - - - - - -*/ diff --git a/docs/doxygen/other/thread_affinity.dox b/docs/doxygen/other/thread_affinity.dox deleted file mode 100644 index 405477b8d3..0000000000 --- a/docs/doxygen/other/thread_affinity.dox +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page page_affinity Block Thread Affinity and Priority - -\section affinity Block Thread Affinity - -In the thread-per-block scheduler, you can set the block's core -affinity. Each block can be pinned to a group cores or be set back -to use the standard kernel scheduler. - -The implementation is done by adding new functions to the threading -section of the gnuradio-runtime library: - -\code - gr::thread::gr_thread_t get_current_thread_id(); - void thread_bind_to_processor(unsigned int n); - void thread_bind_to_processor(const std::vector<unsigned int> &mask); - void thread_bind_to_processor(gr::thread::gr_thread_t thread, unsigned int n); - void thread_bind_to_processor(gr::thread::gr_thread_t thread, const std::vector<unsigned int> &mask); - void thread_unbind(); - void thread_unbind(gr::thread::gr_thread_t thread); -\endcode - -The ability to set a thread's affinity to a core or groups of cores is -not implemented in the Boost thread library, and so we have made our -own portability library. In particular, the gr::thread::gr_thread_t type is -defined as the thread type for the given system. The other functions -are designed to be portable as well by calling the specific -implementation for the thread affinity for a particular platform. - -There are functions to set a thread to a group of cores. If the thread -is not given, the current thread is used. If a single number is -passed, only that core is set (this is equivalent to a core mask with -just a single value). - -Similarly, there are functions to unset the affinity. This practically -implements the setting of the thread's affinity to all possible -cores. Again, the function that does not take a thread argument unsets -the affinity for the current thread. - -NOTE: Not available on OSX. - - -\subsection affinity_api GNU Radio Block API - -Each block has two new data members: - -- threaded: a boolean value that is true if the block is attached to a - thread. -- thread: a gr::thread::gr_thread_t handle to the block's thread. - -A block can set and unset its affinity at any time using the -following member functions: - -- gr::block::set_processor_affinity(const std::vector<int> &mask) -- gr::block::unset_processor_affinity() - -Where \p mask is a vector of core numbers to set the thread's affinity -to. - -The current core affinity can be retrieved using the member function: - -- gr::block::processor_affinity() - -When set before the flowgraph is started, the scheduler will set the -thread's affinity when it is started. When already running, the -block's affinity will be immediately set. - - -\subsection affinity_api_hier Setting Affinity for a gr::hier_block2 - -A hierarchical block (gr::hier_block2) also has a concept of setting -the block thread affinity. Because the hierarchical block itself does -no work and just encapsulates a set of blocks, setting the -hierarchical block's affinity individually sets all blocks inside it -to that affinity setting. - -The gr::hier_block2 class supports the same API interface to the block -thread affinity: - -- gr::hier_block2::set_processor_affinity(const std::vector<int> &mask) -- gr::hier_block2::unset_processor_affinity() -- gr::hier_block2::processor_affinity() - -Setting and unsetting the affinity does so recursively for every block -in the hierarchical block. It is of course possible to individually set -the affinity to any block underneath the hierarchical block. However, -in this case, note that when asking for the current affinity value -using 'processor_affinity()', the code returns the current processor -affinity value of only the first block. - - -\subsection affinity_api_grc GRC Access - -GRC supports the setting of the thread core affinity in a block's -options. Each block now has a field 'Core Affinity' that accepts a -vector (list) of integers and sets the affinity after the block is -constructed. - -Note that GRC does not provide a callback function for changing the -thread core affinity while the flowgraph is running. - - - -\section priority_api Setting Thread Priority - -Similarly to setting the core affinity for a given block, we can also -set the thread priority. This concept adds three new function calls to -all blocks: - -\li gr::block::set_thread_priority(int priority): Sets the current thread priority. -\li gr::block::active_thread_priority(): Gets the active priority for the thread. -\li gr::block::thread_priority(): Gets the stored thread priority. - -The range of the thread priority might be system dependent, so look to -your system/OS documentation. Linux specifies this range in -\b sched_setscheduler as a value between 1 and 99 where 1 is the lowest -priority and 99 is the highest. POSIX systems can retrieve these min -and max values using \b sched_get_priority_min and \b -sched_get_priority_max and may only allow 32 distinct values to be -set. - -NOTE: Not available on Windows or OSX. - -*/ diff --git a/docs/doxygen/other/usage.dox b/docs/doxygen/other/usage.dox index d0872c0a03..c02b88c3a0 100644 --- a/docs/doxygen/other/usage.dox +++ b/docs/doxygen/other/usage.dox @@ -1,4 +1,4 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. +# Copyright (C) 2018 Free Software Foundation, Inc. # # Permission is granted to copy, distribute and/or modify this document # under the terms of the GNU Free Documentation License, Version 1.3 @@ -9,44 +9,39 @@ /*! \page page_usage Usage Manual -Note: Once built, check out <a href="https://www.gnuradio.org/" target="_blank">gnuradio.org</a> for -more tutorials on using the software system and examples. +Note: The Usage Manual has been moved to the <a href="https://wiki.gnuradio.org" target="_blank">GNU Radio wiki</a>, along with all other documentation that is not specific to a block/class/function/etc. -<b>Getting Started</b> +For viewing offline, snapshots of these wiki pages are periodically exported and stored in gnuradio/docs/usage-manual/ + +Here are links to the various Usage Manual pages (they are also linked on the home page of the wiki): -\li \subpage build_guide - Installation notes, dependencies etc. -\li \subpage page_exploring_gnuradio -\li \subpage page_operating_fg +<b>Getting Started</b> +\li <a href="https://wiki.gnuradio.org/index.php/Handling_Flowgraphs" target="_blank">Handling Flowgraphs</a> +\li <a href="https://wiki.gnuradio.org/index.php/GNURadioCompanion" target="_blank">GNU Radio Companion (GRC)</a> <b>Metadata and Messages</b> -\li \subpage page_pmt -\li \subpage page_metadata -\li \subpage page_msg_passing -\li \subpage page_stream_tags -\li \subpage page_tagged_stream_blocks +\li <a href="https://wiki.gnuradio.org/index.php/Polymorphic_Types_(PMTs)" target="_blank">Polymorphic Types (PMTs)</a> +\li <a href="https://wiki.gnuradio.org/index.php/Metadata_Information" target="_blank">Metadata Information</a> +\li <a href="https://wiki.gnuradio.org/index.php/Message_Passing" target="_blank">Message Passing</a> +\li <a href="https://wiki.gnuradio.org/index.php/Stream_Tags" target="_blank">Stream Tags</a> +\li <a href="https://wiki.gnuradio.org/index.php/Tagged_Stream_Blocks" target="_blank">Tagged Stream Blocks</a> <b>Advanced Development Topics</b> -\li \subpage page_logger -\li \subpage page_perf_counters -\li \subpage page_affinity -\li \subpage page_prefs -\li \subpage page_python_blocks +\li <a href="https://wiki.gnuradio.org/index.php/Logging" target="_blank">Logging</a> +\li <a href="https://wiki.gnuradio.org/index.php/Performance_Counters" target="_blank">Performance Counters</a> +\li <a href="https://wiki.gnuradio.org/index.php/Block_Thread_Affinity_and_Priority" target="_blank">Block Thread Affinity and Priority</a> +\li <a href="https://wiki.gnuradio.org/index.php/Configuration_Files" target="_blank">Configuration Files</a> +\li <a href="https://wiki.gnuradio.org/index.php/Types_of_Blocks" target="_blank">Types of Blocks</a> <b>Signal Processing and Digital Communications</b> -\li \subpage page_pfb -\li \subpage page_ofdm -\li \subpage page_packet_data - -<b>Out-of-tree Modules</b> - -\li \subpage page_oot_config +\li <a href="https://wiki.gnuradio.org/index.php/Polyphase_Filterbanks" target="_blank">Polyphase Filterbanks</a> <b>VOLK</b> -\li \subpage volk_guide - How to incorporate and use VOLK in GNU Radio blocks +\li <a href="https://wiki.gnuradio.org/index.php/VOLK_Guide" target="_blank">VOLK Guide</a> */ diff --git a/docs/doxygen/other/volk_guide.dox b/docs/doxygen/other/volk_guide.dox deleted file mode 100644 index 1f8f128e4b..0000000000 --- a/docs/doxygen/other/volk_guide.dox +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright (C) 2017 Free Software Foundation, Inc. -# -# Permission is granted to copy, distribute and/or modify this document -# under the terms of the GNU Free Documentation License, Version 1.3 -# or any later version published by the Free Software Foundation; -# with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. -# A copy of the license is included in the section entitled "GNU -# Free Documentation License". - -/*! \page volk_guide Instructions for using VOLK in GNU Radio - -Note: Many blocks have already been converted to use VOLK in their calls, so -they can also serve as examples. See the -gr::blocks::complex_to_<type>.h files for examples of various blocks -that make use of VOLK. - -\section volk_intro Introduction - -VOLK is the Vector-Optimized Library of Kernels. It is a library that -contains kernels of hand-written SIMD code for different mathematical -operations. Since each SIMD architecture can be greatly different and -no compiler has yet come along to handle vectorization properly or -highly efficiently, VOLK approaches the problem differently. For each -architecture or platform that a developer wishes to vectorize for, a -new proto-kernel is added to VOLK. At runtime, VOLK will select the -correct proto-kernel. In this way, the users of VOLK call a kernel for -performing the operation that is platform/architecture agnostic. This -allows us to write portable SIMD code. - -VOLK kernels are always defined with a 'generic' proto-kernel, which -is written in plain C. With the generic kernel, the kernel becomes -portable to any platform. Kernels are then extended by adding -proto-kernels for new platforms in which they are desired. - -A good example of a VOLK kernel with multiple proto-kernels defined is -the volk_32f_s32f_multiply_32f_a. This kernel implements a scalar -multiplication of a vector of floating point numbers (each item in the -vector is multiplied by the same value). This kernel has the following -proto-kernels that are defined for 'generic,' 'avx,' 'sse,' and 'neon' - -\code - void volk_32f_s32f_multiply_32f_a_generic - void volk_32f_s32f_multiply_32f_a_sse - void volk_32f_s32f_multiply_32f_a_avx - void volk_32f_s32f_multiply_32f_a_neon -\endcode - -These proto-kernels means that on platforms with AVX support, VOLK can -select this option or the SSE option, depending on which is faster. If -all else fails, VOLK can fall back on the generic proto-kernel, which -will always work. - -See <a -href="http://libvolk.org</a> for details on the VOLK naming scheme. - - -\section volk_alignment Setting and Using Memory Alignment Information - -For VOLK to work as best as possible, we want to use memory-aligned -SIMD calls, which means we have to have some way of knowing and -controlling the alignment of the buffers passed to gr_block's work -function. We set the alignment requirement for SIMD aligned memory -calls with: - -\code - const int alignment_multiple = - volk_get_alignment() / output_item_size; - set_alignment(std::max(1,alignment_multiple)); -\endcode - -The VOLK function 'volk_get_alignment' provides the alignment of the -the machine architecture. We then base the alignment on the number of -output items required to maintain the alignment, so we divide the -number of alignment bytes by the number of bytes in an output items -(sizeof(float), sizeof(gr_complex), etc.). This value is then set per -block with the 'set_alignment' function. - -Because the scheduler tries to optimize throughput, the number of -items available per call to work will change and depends on the -availability of the read and write buffers. This means that it -sometimes cannot produce a buffer that is properly memory -aligned. This is an inevitable consequence of the scheduler -system. Instead of requiring alignment, the scheduler enforces the -alignment as much as possible, and when a buffer becomes unaligned, -the scheduler will work to correct it as much as possible. If a -block's buffers are unaligned, then, the scheduler sets a flag to -indicate as much so that the block can then decide what best to -do. The next section discusses the use of the aligned/unaligned -information in a gr_block's work function. - - -\section volk_work Calling VOLK kernels in Work() - -The buffers passed to work/general_work in a gr_block are not -guaranteed to be aligned, but they will mostly be aligned whenever -possible. When not aligned, the 'is_unaligned()' flag will be set so -the scheduler knows to try to realign the buffers. We actually make -calls to the VOLK dispatcher, which is mainly designed to check the -buffer alignments and call the correct version of the kernel for -us. From the user-level view of VOLK, calling the dispatcher allows us -to ignore the concept of aligned versus unaligned. This looks like: - -\code -int -gr_some_block::work (int noutput_items, - gr_vector_const_void_star &input_items, - gr_vector_void_star &output_items) -{ - const float *in = (const float *) input_items[0]; - float *out = (float *) output_items[0]; - - // Call the dispatcher to check alignment and call the _a or _u - // version of the kernel. - volk_32f_something_32f(out, in, noutput_items); - - return noutput_items; -} -\endcode - - - -\section volk_tuning Tuning VOLK Performance - -VOLK comes with a profiler that will build a config file for the best -SIMD architecture for your processor. Run volk_profile that is -installed into $PREFIX/bin. This program tests all known VOLK kernels -for each architecture supported by the processor. When finished, it -will write to $HOME/.volk/volk_config the best architecture for the -VOLK function. This file is read when using a function to know the -best version of the function to execute. - -\subsection volk_hand_tuning Hand-Tuning Performance - -If you know a particular architecture works best for your processor, -you can specify the particular architecture to use in the VOLK -preferences file: $HOME/.volk/volk_config - -The file looks like: - -\code - volk_<FUNCTION_NAME> <ARCHITECTURE> -\endcode - -Where the "FUNCTION_NAME" is the particular function that you want to -over-ride the default value and "ARCHITECTURE" is the VOLK SIMD -architecture to use (generic, sse, sse2, sse3, avx, etc.). For -example, the following config file tells VOLK to use SSE3 for the -aligned and unaligned versions of a function that multiplies two -complex streams together. - -\code - volk_32fc_x2_multiply_32fc_a sse3 - volk_32fc_x2_multiply_32fc_u sse3 -\endcode - -\b Tip: if benchmarking GNU Radio blocks, it can be useful to have a -volk_config file that sets all architectures to 'generic' as a way to -test the vectorized versus non-vectorized implementations. - -*/ |