diff options
Diffstat (limited to 'docs/doxygen/other')
-rw-r--r-- | docs/doxygen/other/ctrlport.dox | 280 | ||||
-rw-r--r-- | docs/doxygen/other/extra_pages.dox | 7 | ||||
-rw-r--r-- | docs/doxygen/other/group_defs.dox | 1 | ||||
-rw-r--r-- | docs/doxygen/other/logger.dox | 69 | ||||
-rw-r--r-- | docs/doxygen/other/main_page.dox | 57 | ||||
-rw-r--r-- | docs/doxygen/other/metadata.dox | 45 | ||||
-rw-r--r-- | docs/doxygen/other/msg_passing.dox | 18 | ||||
-rw-r--r-- | docs/doxygen/other/packet_txrx.dox | 82 | ||||
-rw-r--r-- | docs/doxygen/other/pfb_intro.dox | 14 | ||||
-rw-r--r-- | docs/doxygen/other/pmt.dox | 190 | ||||
-rw-r--r-- | docs/doxygen/other/thread_affinity.dox | 8 |
11 files changed, 579 insertions, 192 deletions
diff --git a/docs/doxygen/other/ctrlport.dox b/docs/doxygen/other/ctrlport.dox new file mode 100644 index 0000000000..69f467b288 --- /dev/null +++ b/docs/doxygen/other/ctrlport.dox @@ -0,0 +1,280 @@ +/*! \page page_ctrlport ControlPort + +\section Introduction + +This is the gr-ctroport package. It is a tool to create distributed +contol applications for GNU Radio. It provides blocks that can be +connected to an output stream to plot the signal remotely. It also +provides an API that allows blocks to export variables that can be +set, monitored, and plotted remotely. + +The Python namespace is in gnuradio.ctrlport, which would be normally +imported as: + +\code + from gnuradio import ctrlport +\endcode + + +See the Doxygen documentation for details about the blocks available +in this package. A quick listing of the details can be found in Python +after importing by using: + +\code + help(ctrlport) +\endcode + +\section Dependencies + +ControlPort requires ZeroC's ICE and associated +libraries/headers/programs. ICE is generally installed into the +standard paths if using a software repo (like apt-get, yum, etc.). If +installed by hand, GNU Radio assumes ICE is installed into +/opt/Ice-3.4.2. If this is not the case, you can tell GNU Radio where +to find ICE by passing to cmake the following: + + -DICE_MANUAL_INSTALL_PATH=\<your path here\> + +\section conf Configuration + +ControlPort is configured using two files. The first is the GNU Radio +preferences configuration while the second file is specific to the +type of transport engine used. Since we are focusing on using ICE, the +configuration file is the ICE configuration file and format. + +The GNU Radio preferences file allows you to enable or disable +ControlPort. If enabled and a configuration file is used, this file +also specifies the location of the configuration file. + +\code + [ControlPort] + on = True + config = ctrlport.conf +\endcode + +The 'ctrlport.conf' holds specific properties related to the transport +engine. If using ICE, more information can be found here: +http://doc.zeroc.com/display/Ice/Properties+and+Configuration + +An example ICE config file is installed with GNU Radio to show how to +change the exposed endpoint of ControlPort. This file is installed +as ${prefix}/etc/gnuradio/ctrlport.conf.example. + + +\section using Using ControlPort to Export Variables + +The ability to export variables from a block is inherited from +gr_block. Then, when the flowgraph is started, the function +<b>setup_rpc()</b> is called in turn for each block. If the block +defines and exports variables using <b>setup_rpc()</b>, then they are +now all available over ControlPort. + +The new block simply declares that it is overloading +<b>setup_rpc()</b> in its header file. In the source file, it defines +any setter and/or getting for a variable it wants. + +Say we have a class <b>gr::blocks::foo</b> that has variables <b>a</b> +and <b>b</b> that we want to export. Specifically, we want to be able +to read the values of both <b>a</b> and <b>b</b> and also set the +value of <b>b</b>. The class <b>gr::blocks::foo</b> has setters and +getters all set up. So our class declaration looks something like: + +\code +namespace gr { + namespace blocks { + + class foo_impl : public foo + { + private: + float d_a, d_b; + + public: + foo_impl(float a, float b); + ~foo_impl(); + + float a() const { return d_a; } + float b() const { return d_a; } + void set_a(float a) { d_a = a; } + void set_b(float b) { d_b = b; } + void setup_rpc(); + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +The source code then sets up the class and fills in +<b>setup_rpc()</b>. + +\code +namespace gr { + namespace blocks { + + foo_impl::foo_impl(float a, float b): + gr_sync_bloc(....), + d_a(a), d_b(b) + { } + + foo_impl::~foo_impl() + { } + + void + foo_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "a", + &foo::a, + pmt::mp(-2.0f), pmt::mp(2.0f), pmt::mp(0.0f), + "", "Get value of a", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<foo, float>( + alias(), "b", + &foo::b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Get value of b", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<foo, float>( + alias(), "b", + &foo::set_b, + pmt::mp(0.0f), pmt::mp(20.0f), pmt::mp(10.0f), + "", "Set value of b", RPC_PRIVLVL_MIN, + DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + int + foo_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { .... } + + } /* namespace blocks */ +} /* namespace gr */ +\endcode + +In the above example, we're ignoring some of the basic semantics of +the class as a GNU Radio block and focus just on the call to set up +the get and set functions over ControlPort. Each block has a function +that allows us to add a new ControlPort interface object to a list, +the <b>add_rpc_variable</b>. We don't care about that list anymore; +that's for ControlPort to worry about. We just add new variables, +either setters or getters. + +Without dissecting every piece of the above calls, notice that we use +the public class, <b>gr::blocks::foo</b> as the class, not the +implementation class. We also use the block's alias, which GNU Radio +uses as a database entry to connect a block by name to the pointer in +memory. This allows ControlPort to know where the object in memory is +at any given time to access the setters and getters. + +The three PMTs specified are simply an expected minimum, maximum, and +default value. None of these are strictly enforced and only serve as +guides. The RPC_PRIVLVL_MIN is currently a placeholder for a +privilege level setting. In many cases, reading <b>b</b> might be +fine for everyone, but we want strong restrictions on who has the +ability to set <b>b</b>. + +And finally, we can specify display options to hint at the right way +to display this variable when remotely plotting it. More on that in +the following section. + +Finally, note that we put \#ifdefs around the code. We always want +<b>setup_rpc</b> to be there and callable, but if ControlPort was not +built for GNU Radio, we cannot register any variables with it. This is +just a nicety to allow us to set up our code for use with ControlPort +without requiring it. + + +\subsection alt_reg Alternative Registers + +If using the concept above, <b>setup_rpc</b> automatically gets called +when the flowgraph is started. In most instances, this is all we ever +need since there's nothing interesting going on until then. However, +if not using a gr_block or needing access before we run the flowgraph, +the above method won't work (it comes down to when the block's alias +has meaning). + +There are alternate variable registration functions for the sets and +gets. These take the form: + +\code + rpcbasic_register_get(const std::string& name, + const char* functionbase, + T* obj, + Tfrom (T::*function)(), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) + + rpcbasic_register_set(const std::string& name, + const char* functionbase, + T* obj, + void (T::*function)(Tto), + const pmt::pmt_t &min, const pmt::pmt_t &max, const pmt::pmt_t &def, + const char* units_ = "", + const char* desc_ = "", + priv_lvl_t minpriv_ = RPC_PRIVLVL_MIN, + DisplayType display_ = DISPNULL) +\endcode + +The only thing different about the above code is that instead of +taking a single 'alias()' name, which provides us access to the +objects pointer, we instead provide a unique name +(<b>fucntionbase</b>) and a pointer to the object itself +(<b>obj</b>). These are templated functions, so the class T is known +from that. + +If using this method, the recommended way is to create a new function +(not <b>setup_rpc</b>), register the variable using +<b>add_rpc_variable</b> but with the different <b>register_get/set</b> +shown here, and then call this function either in the object's +constructor or make it a public member function to be called when you +need it. + + +\section disp Display Options + +When exporting a new RPC variable over ControlPort, one argument is a +display options mask. These options are useful to a remote client to +tell identify activities like default plotters and initial +conditions. The gr-ctrlport-monitor application uses this heavily in +determining how to plot ControlPort variables. + +The options mask is just a 32-bit value with options OR'd +together. Certain options are only appropriate for certain types of +plots. Options on plots where that option is not available will +simply be ignored. + +The main caveat to be aware of is that the DISPXY plot type is +specific to complex values. Therefore, DISPOPTCPLX is assumed. + +These options are specified in rpccallbackregister_base.h and are +exposed through SWIG to live in the \b gr namespace. + +<b>Plot Types</b> +\li <b>DISPNULL:</b> Nothing specified. +\li <b>DISPTIME:</b> Time-domain plot. +\li <b>DISPXY:</b> XY or constellation plot (complex only). +\li <b>DISPPSD:</b> PSD plot. +\li <b>DISPSPEC:</b> Spectrogram plot. +\li <b>DISPRAST:</b> Time raster plot (non-complex only) + +<b>Plot Options</b> +\li <b>DISPOPTCPLX:</b> Signal is complex. +\li <b>DISPOPTLOG:</b> Start plot in semilog-y mode (time domain only). +\li <b>DISPOPTSTEM:</b> Start plot in stem mode (time domain only). +\li <b>DISPOPTSTRIP:</b> Run plot as a stripchart (time domain only). +\li <b>DISPOPTSCATTER:</b> Do scatter plot instead of lines (XY plot only). + +*/ diff --git a/docs/doxygen/other/extra_pages.dox b/docs/doxygen/other/extra_pages.dox index 94c741864d..617e00d553 100644 --- a/docs/doxygen/other/extra_pages.dox +++ b/docs/doxygen/other/extra_pages.dox @@ -70,9 +70,6 @@ audio-osx and audio-windows to be either satisfied or built. \subsection dep_uhd uhd: The Ettus USRP Hardware Driver Interface \li uhd (>= 3.0.0) http://code.ettus.com/redmine/ettus/projects/uhd/wiki -\subsection dep_shd shd: The Symplex Hardware Driver Interface -\li shd (>= 3.0.0) - \subsection dep_gr_video_sdl gr-video-sdl: PAL and NTSC display \li SDL (>= 1.2.0) http://www.libsdl.org/download-1.2.php @@ -122,11 +119,11 @@ built or installed. The -DENABLE_DEFAULT=False can be used to disable all components. Individual components can then be selectively turned back -on. For example, just buidling the Volk and Gruel libraries can be +on. For example, just buidling the Volk library can be done with this: \code -cmake -DENABLE_DEFAULT=Off -DENABLE_VOLK=True -DENABLE_GRUEL=True <srcdir> +cmake -DENABLE_DEFAULT=Off -DENABLE_VOLK=True <srcdir> \endcode diff --git a/docs/doxygen/other/group_defs.dox b/docs/doxygen/other/group_defs.dox index 1c96abf3dc..170465fa1c 100644 --- a/docs/doxygen/other/group_defs.dox +++ b/docs/doxygen/other/group_defs.dox @@ -15,6 +15,7 @@ /*! \defgroup channel_models_blk Channel Models */ /*! \defgroup channelizers_blk Channelizers */ /*! \defgroup coding_blk Information Coding and Decoding */ +/*! \defgroup controlport_blk ControlPort */ /*! \defgroup debug_tools_blk Debug Tools */ /*! \defgroup deprecated_blk Deprecated */ /*! \defgroup equalizers_blk Equalizers */ diff --git a/docs/doxygen/other/logger.dox b/docs/doxygen/other/logger.dox index 22458051db..3eb487d9af 100644 --- a/docs/doxygen/other/logger.dox +++ b/docs/doxygen/other/logger.dox @@ -24,12 +24,13 @@ different levels. These levels are: 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 WARN level, all WARN and -higher messages are logged while DEBUG and INFO are ignored. +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-core.conf file +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 @@ -82,7 +83,7 @@ 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>"); + gr::LOG_<level>(<logger>, "<Message to print>"); \endcode Where \<level\> is one of the levels as mentioned above, \<logger\> is @@ -92,8 +93,8 @@ 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"); + 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 @@ -114,15 +115,15 @@ 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"); + 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 @@ -143,7 +144,7 @@ provided with GNU Radio can be used. After installation, the default configuration script is located at: <pre> - $prefix/etc/gnuradio/gr_log_default.xml + $prefix/etc/gnuradio/gr_log_default.conf </pre> For the following examples, we will assume that our local @@ -151,12 +152,12 @@ For the following examples, we will assume that our local \code [LOG] -log_config = /opt/gr/etc/gnuadio/gr_log_default.xml +log_config = /opt/gr/etc/gnuadio/gr_log_default.conf log_level = debug debug_level = Off \endcode -Inside of the XML default configuration file, we define the parameters +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 @@ -187,19 +188,39 @@ 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): +except we would use "gr_log_debug." in the gr::LOG_GETLOGGER call): \code - gr_prefs *p = gr_prefs::singleton(); + 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); + 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")'. +input to our logging macros like 'gr::LOG_INFO(LOG, "message")'. +\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_logger_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 gr_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/main_page.dox b/docs/doxygen/other/main_page.dox index 916ac9bdd1..8e35cb3c87 100644 --- a/docs/doxygen/other/main_page.dox +++ b/docs/doxygen/other/main_page.dox @@ -46,6 +46,7 @@ More details on GNU Radio concepts: \li \ref volk_guide \li \ref page_pfb \li \ref page_tagged_stream_blocks +\li \ref page_packet_data \section flowgraph Operating a Flowgraph @@ -70,7 +71,7 @@ done. A single source and sink are used with a FIR filter between them. \code - from gnuradio import gr, filter + from gnuradio import gr, filter, analog class my_topblock(gr.top_block): def __init__(self): @@ -79,9 +80,9 @@ them. amp = 1 taps = filter.firdes.low_pass(1, 1, 0.1, 0.01) - self.src = gr.noise_source_c(gr.GR_GAUSSIAN, amp) + self.src = analog.noise_source_c(gr.GR_GAUSSIAN, amp) self.flt = filter.fir_filter_ccf(1, taps) - self.snk = gr.null_sink(gr.sizeof_gr_complex) + self.snk = blocks.null_sink(gr.sizeof_gr_complex) self.connect(self.src, self.flt, self.snk) @@ -203,7 +204,7 @@ buffer, which may be different than what was initially specified. 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_add_const_cc block can be done while the flowgraph is +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 @@ -211,23 +212,24 @@ 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_noise_source_c blocks and then replaces the gr_add_cc block with a -gr_sub_cc block to then subtract the sources. +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 +from gnuradio import gr, analog, blocks import time class mytb(gr.top_block): def __init__(self): gr.top_block.__init__(self) - self.src0 = gr.noise_source_c(gr.GR_GAUSSIAN, 1) - self.src1 = gr.noise_source_c(gr.GR_GAUSSIAN, 1) - self.add = gr.add_cc() - self.sub = gr.sub_cc() - self.head = gr.head(gr.sizeof_gr_complex, 1000000) - self.snk = gr.file_sink(gr.sizeof_gr_complex, "output.32fc") + 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)) @@ -270,19 +272,19 @@ The following example expands the previous example but sets and resets the max noutput_items both locally and globally. \code -from gnuradio import gr +from gnuradio import gr, analog, blocks import time class mytb(gr.top_block): def __init__(self): gr.top_block.__init__(self) - self.src0 = gr.noise_source_c(gr.GR_GAUSSIAN, 1) - self.src1 = gr.noise_source_c(gr.GR_GAUSSIAN, 1) - self.add = gr.add_cc() - self.sub = gr.sub_cc() - self.head = gr.head(gr.sizeof_gr_complex, 1000000) - self.snk = gr.file_sink(gr.sizeof_gr_complex, "output.32fc") + 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)) @@ -327,8 +329,9 @@ The \ref volk_guide page provides an overview of how to incorporate and use Volk in GNU Radio blocks. Many blocks have already been converted to use Volk in their calls, so -they can also serve as examples. See the gr_complex_to_xxx.h file for -examples of various blocks that make use of Volk. +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 prefs Configuration / Preference Files @@ -374,10 +377,10 @@ Similarly, in C++, we get a reference to the object by explicitly calling for the singleton of the object: \code - gr_prefs *p = gr_prefs::singleton(); + prefs *p = prefs::singleton(); \endcode -The methods associated with this preferences object are (from class gr_prefs): +The methods associated with this preferences object are (from class gr::prefs): \code bool has_section(string section) @@ -411,9 +414,9 @@ config file itself. New as of 3.6.5. Using gr_modtool, each package comes with the ability to easily locate -the gnuradio-core library using the 'find_package(GnuradioCore)' cmake -command. This only locates that the library and include directories -exist, which is enough for most simple projects. +the gnuradio-runtime library using the 'find_package(GnuradioRuntime)' +cmake command. This only locates that the library and include +directories exist, 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 diff --git a/docs/doxygen/other/metadata.dox b/docs/doxygen/other/metadata.dox index b527b21008..03ebe591e4 100644 --- a/docs/doxygen/other/metadata.dox +++ b/docs/doxygen/other/metadata.dox @@ -106,7 +106,7 @@ interface. 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::pmt_make_dict) of key:value pairs, then the dictionary is +(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 @@ -117,27 +117,26 @@ we use PMT operators. For example, we create a simplified version of the header in C++ like this: \code - using namespace pmt; const char METADATA_VERSION = 0x0; - pmt_t header; - header = pmt_make_dict(); - header = pmt_dict_add(header, mp("version"), mp(METADATA_VERSION)); - header = pmt_dict_add(header, mp("rx_rate"), mp(samp_rate)); - std::string hdr_str = pmt_serialize_str(header); + 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::pmt_dict_add adds a new key:value pair to the +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 +call the correct 'pmt::from_xxx' function. For more direct control over the data type, see PMT functions in pmt.h, such as -pmt::pmt_from_uint64 or pmt::pmt_from_double. +pmt::from_uint64 or pmt::from_double. -We finish this off by using pmt::pmt_serialize_str to convert the PMT +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. @@ -153,10 +152,10 @@ 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_t hdr = pmt_deserialize_str(str); - if(pmt_dict_has_key(hdr, pmt_string_to_symbol("strt"))) { - pmt_t r = pmt_dict_ref(hdr, pmt_string_to_symbol("strt"), PMT_NIL); - uint64_t seg_start = pmt_to_uint64(r); + 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 @@ -221,7 +220,7 @@ 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::pmt_dict_add function to insert +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. @@ -239,7 +238,7 @@ 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::pmt_print on any PMT object to print it to +ability to do pmt::print on any PMT object to print it to screen. Before converting from a PMT to it's natural data type, it is necessary to know the data type. @@ -310,15 +309,15 @@ into the metadata to keep track of later. The date in this case is encoded as a vector of uint16 with [day, month, year]. \code - from gruel import pmt + import pmt from gnuradio import blocks - key = pmt.pmt_intern("date") - val = pmt.pmt_init_u16vector(3, [13,12,2012]) + key = pmt.intern("date") + val = pmt.init_u16vector(3, [13,12,2012]) - extras = pmt.pmt_make_dict() - extras = pmt.pmt_dict_add(extras, key, val) - extras_str = pmt.pmt_serialize_str(extras) + 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, diff --git a/docs/doxygen/other/msg_passing.dox b/docs/doxygen/other/msg_passing.dox index aea0ac94ae..dc0d5bbb3f 100644 --- a/docs/doxygen/other/msg_passing.dox +++ b/docs/doxygen/other/msg_passing.dox @@ -83,7 +83,7 @@ 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::pmt_intern function as pmt.pmt_intern("string")). +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: @@ -157,13 +157,13 @@ void gr_message_debug::print(pmt::pmt_t msg) { std::cout << "***** MESSAGE DEBUG PRINT ********\n"; - pmt::pmt_print(msg); + pmt::print(msg); std::cout << "**********************************\n"; } \endcode The function simply takes in the PMT message and prints it. The method -pmt::pmt_print is a function in the PMT library to print the +pmt::print is a function in the PMT library to print the PMT in a friendly, (mostly) pretty manner. The gr_tagged_stream_to_pdu block only defines a single @@ -196,11 +196,11 @@ gr_tagged_stream_to_pdu::send_message function that is shown below. void gr_tagged_stream_to_pdu::send_meassage() { - if(pmt::pmt_length(d_pdu_vector) != d_pdu_length) { + if(pmt::length(d_pdu_vector) != d_pdu_length) { throw std::runtime_error("msg length not correct"); } - pmt::pmt_t msg = pmt::pmt_cons(d_pdu_meta, + pmt::pmt_t msg = pmt::cons(d_pdu_meta, d_pdu_vector); message_port_pub(pdu_port_id, msg); @@ -247,9 +247,9 @@ flowgraph. These PDUs could come from another block or flowgraph, but here, we will create and insert them by hand. \code - port = pmt.pmt_intern("pdus") - msg = pmt.pmt_cons(pmt.PMT_NIL, - pmt.pmt_make_u8vector(16, 0xFF)) + port = pmt.intern("pdus") + msg = pmt.cons(pmt.PMT_NIL, + pmt.make_u8vector(16, 0xFF)) src.to_basic_block()._post(port, msg) \endcode @@ -264,6 +264,6 @@ 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 gnuradio-core/src/examples/msg_passing. +through GRC in gr-blocks/examples/msg_passing. */ diff --git a/docs/doxygen/other/packet_txrx.dox b/docs/doxygen/other/packet_txrx.dox new file mode 100644 index 0000000000..afe84240ff --- /dev/null +++ b/docs/doxygen/other/packet_txrx.dox @@ -0,0 +1,82 @@ +/*! \page page_packet_data Packet Data Transmission + +\section intro 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 packetstructure 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 te 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 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 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/pfb_intro.dox b/docs/doxygen/other/pfb_intro.dox index 4f77e7cf76..43e27b4a3c 100644 --- a/docs/doxygen/other/pfb_intro.dox +++ b/docs/doxygen/other/pfb_intro.dox @@ -29,9 +29,9 @@ filter that is then split up between the \p N channels of the PFB. self._fs = 9000 # input sample rate self._M = 9 # Number of channels to channelize - self._taps = gr.firdes.low_pass_2(1, self._fs, 475.50, 50, - attenuation_dB=100, - window=gr.firdes.WIN_BLACKMAN_hARRIS) + 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 @@ -63,9 +63,9 @@ 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>gnuradio-core/src/python/gnuradio/blks2impl/pfb_arb_resampler.py</b>, -which is exposed in Python as <b>blks2.pfb_arb_resampler_ccf</b> and -<b>blks2.pfb_arb_resampler_fff</b>. This block is set up so that the +<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 @@ -90,6 +90,6 @@ channels. NOTE: you need the Scipy and Matplotlib Python modules installed to run this example. -\include gnuradio-core/src/examples/pfb/channelize.py +\include gr-filter/examples/channelize.py */ diff --git a/docs/doxygen/other/pmt.dox b/docs/doxygen/other/pmt.dox index 61b73bca13..04f58aafc8 100644 --- a/docs/doxygen/other/pmt.dox +++ b/docs/doxygen/other/pmt.dox @@ -66,25 +66,25 @@ 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::pmt_from_<type>". Similarly, when getting data out of a -PMT, we use a call like "pmt::pmt_to_<type>". For example: +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::pmt_from_double(a); -double b = pmt::pmt_to_double(pmt_a); +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::pmt_from_long(c); -int d = pmt::pmt_to_long(pmt_c); +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::pmt_make_rectangular(a.real(), b.imag()); -std::complex<double> b = pmt::pmt_to_complex(pmt_a); +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 @@ -100,17 +100,17 @@ 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::pmt_intern, is simply an alias of the first. +function, pmt::intern, is simply an alias of the first. \code -pmt::pmt_t str0 = pmt::pmt_string_to_symbol(std::string("some string")); -pmt::pmt_t str1 = pmt::pmt_intern(std::string("some string")); +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::pmt_symbol_to_string(str0); +std::string s = pmt::symbol_to_string(str0); \endcode @@ -118,28 +118,28 @@ std::string s = pmt::pmt_symbol_to_string(str0); 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::pmt_is_<type>". We can use these to test the PMT before trying +"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::pmt_string_to_symbol(std::string("some string")); -if(pmt::pmt_is_symbol(str0)) - std::string s = pmt::pmt_symbol_to_string(str0); +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::pmt_from_double(a); -if(pmt::pmt_is_double(pmt_a)) - double b = pmt::pmt_to_double(pmt_a); +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::pmt_from_long(c); -if(pmt::pmt_is_long(pmt_a)) - int d = pmt::pmt_to_long(pmt_c); +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::pmt_is_double(pmt_a)) - double d = pmt::pmt_to_double(pmt_c); +if(pmt::is_double(pmt_a)) + double d = pmt::to_double(pmt_c); \endcode @@ -156,15 +156,15 @@ 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::pmt_is_dict(const pmt_t &obj) -- pmt_t pmt::pmt_make_dict() -- pmt_t pmt::pmt_dict_add(const pmt_t &dict, const pmt_t &key, const pmt_t &value) -- pmt_t pmt::pmt_dict_delete(const pmt_t &dict, const pmt_t &key) -- bool pmt::pmt_dict_has_key(const pmt_t &dict, const pmt_t &key) -- pmt_t pmt::pmt_dict_ref(const pmt_t &dict, const pmt_t &key, const pmt_t ¬_found) -- pmt_t pmt::pmt_dict_items(pmt_t dict) -- pmt_t pmt::pmt_dict_keys(pmt_t dict) -- pmt_t pmt::pmt_dict_values(pmt_t dict) +- 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 @@ -173,41 +173,41 @@ reference to the old dictionary. This just keeps our number of variables small. \code -from gruel import pmt +import pmt -key0 = pmt.pmt_intern("int") -val0 = pmt.pmt_from_long(123) -val1 = pmt.pmt_from_long(234) +key0 = pmt.intern("int") +val0 = pmt.from_long(123) +val1 = pmt.from_long(234) -key1 = pmt.pmt_intern("double") -val2 = pmt.pmt_from_double(5.4321) +key1 = pmt.tern("double") +val2 = pmt.om_double(5.4321) # Make an empty dictionary -a = pmt.pmt_make_dict() +a = pmt.make_dict() # Add a key:value pair to the dictionary -a = pmt.pmt_dict_add(a, key0, val0) -pmt.pmt_print(a) +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.pmt_dict_add(a, key0, val1) -pmt.pmt_print(a) +a = pmt.dict_add(a, key0, val1) +print a # Add a new key:value pair -a = pmt.pmt_dict_add(a, key1, val2) -pmt.pmt_print(a) +a = pmt.dict_add(a, key1, val2) +print a # Test if we have a key, then delete it -print pmt.pmt_dict_has_key(a, key1) -a = pmt.pmt_dict_delete(a, key1) -print pmt.pmt_dict_has_key(a, key1) +print pmt.dict_has_key(a, key1) +a = pmt.dict_delete(a, key1) +print pmt.dict_has_key(a, key1) -ref = pmt.pmt_dict_ref(a, key0, pmt.PMT_NIL) -pmt.pmt_print(ref) +ref = pmt.dict_ref(a, key0, pmt.PMT_NIL) +print ref # The following should never print -if(pmt.pmt_dict_has_key(a, key0) and pmt.pmt_eq(ref, pmt.PMT_NIL)): +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 @@ -232,29 +232,29 @@ 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::pmt_length. +pmt::length. For standard vectors, these functions look like: -- bool pmt::pmt_is_vector(pmt_t x) -- pmt_t pmt::pmt_make_vector(size_t k, pmt_t fill) -- pmt_t pmt::pmt_vector_ref(pmt_t vector, size_t k) -- void pmt::pmt_vector_set(pmt_t vector, size_t k, pmt_t obj) -- void pmt::pmt_vector_fill(pmt_t vector, pmt_t fill) +- 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::pmt_is_(dtype)vector(pmt_t x) -- pmt_t pmt::pmt_make_(dtype)vector(size_t k, (dtype) fill) -- pmt_t pmt::pmt_init_(dtype)vector(size_t k, const (dtype*) data) -- pmt_t pmt::pmt_init_(dtype)vector(size_t k, const std::vector<dtype> data) -- pmt_t pmt::pmt_(dtype)vector_ref(pmt_t vector, size_t k) -- void pmt::pmt_(dtype)vector_set(pmt_t vector, size_t k, (dtype) x) -- const dtype* pmt::pmt_(dtype)vector_elements(pmt_t vector, size_t &len) -- dtype* pmt::pmt_(dtype)vector_writable_elements(pmt_t vector, size_t &len) +- 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 @@ -276,12 +276,12 @@ 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::pmt_is_pair (const pmt_t &obj): Return true if obj is a pair, else false -- pmt_t pmt::pmt_cons(const pmt_t &x, const pmt_t &y): construct new pair -- pmt_t pmt::pmt_car(const pmt_t &pair): get the car of the pair (first object) -- pmt_t pmt::pmt_cdr(const pmt_t &pair): get the cdr of the pair (second object) -- void pmt::pmt_set_car(pmt_t pair, pmt_t value): Stores value in the car field -- void pmt::pmt_set_cdr(pmt_t pair, pmt_t value): Stores value in the cdr field +- 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 serdes Serializing and Deserializing @@ -293,38 +293,38 @@ 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::pmt_serialize(pmt_t obj, std::streambuf &sink) -- std::string pmt::pmt_serialize_str(pmt_t obj) -- pmt_t pmt::pmt_deserialize(std::streambuf &source) -- pmt_t pmt::pmt_deserialize_str(std::string str) +- 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 -from gruel import pmt +import pmt -key0 = pmt.pmt_intern("int") -val0 = pmt.pmt_from_long(123) +key0 = pmt.intern("int") +val0 = pmt.from_long(123) -key1 = pmt.pmt_intern("double") -val1 = pmt.pmt_from_double(5.4321) +key1 = pmt.intern("double") +val1 = pmt.from_double(5.4321) # Make an empty dictionary -a = pmt.pmt_make_dict() +a = pmt.make_dict() # Add a key:value pair to the dictionary -a = pmt.pmt_dict_add(a, key0, val0) -a = pmt.pmt_dict_add(a, key1, val1) +a = pmt.dict_add(a, key0, val0) +a = pmt.dict_add(a, key1, val1) -pmt.pmt_print(a) +print a -ser_str = pmt.pmt_serialize_str(a) +ser_str = pmt.serialize_str(a) print ser_str -b = pmt.pmt_deserialize_str(ser_str) -pmt.pmt_print(b) +b = pmt.deserialize_str(ser_str) +print b \endcode @@ -335,13 +335,17 @@ string. This is only done here as a test. \section printing Printing -We have used the pmt::pmt_print function in these examples to nicely -print the contents of a PMT. Another way to 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: +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::pmt_from_double(1.0); +pmt::pmt_t a pmt::from_double(1.0); std::cout << "The PMT a contains " << a << std::endl; \endcode diff --git a/docs/doxygen/other/thread_affinity.dox b/docs/doxygen/other/thread_affinity.dox index 235266febd..2f31d9ce53 100644 --- a/docs/doxygen/other/thread_affinity.dox +++ b/docs/doxygen/other/thread_affinity.dox @@ -6,8 +6,8 @@ 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 GRUEL -library: +The implementation is done by adding new functions to the threading +section of the gnuradio-runtime library: \code gr_thread_t get_current_thread_id(); @@ -21,7 +21,7 @@ library: 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 gruel::gr_thread_t type is +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. @@ -43,7 +43,7 @@ Each block has two new data members: - threaded: a boolean value that is true if the block is attached to a thread. -- thread: a gruel::gr_thread_t handle to the block's thread. +- thread: a gr::thread::gr_thread_t handle to the block's thread. A block can set and unset it's affinity at any time using the following member functions: |