一般的gnuradio开发方式是底层的信号处理模块采用C++编写,这样可以提高处理效率;而模块之间的连接使用Python,这是因为Python提供了更方便的操作接口,使用起来更方便。然而分析gnuradio的源代码可以发现,gnuradio中的流图和顶层模块top_block都是通过C++编写的:
流图类:
/*!
* \brief Class representing a directed, acyclic graph of basic blocks
* \ingroup internal
*/
class GR_RUNTIME_API flowgraph
{
public:
friend GR_RUNTIME_API flowgraph_sptr make_flowgraph();
/*!
* \brief Destruct an arbitrary flowgraph
*/
virtual ~flowgraph();
/*!
* \brief Connect two endpoints
* \details
* Checks the validity of both endpoints, and whether the
* destination is unused so far, then adds the edge to the internal list of
* edges.
*/
void connect(const endpoint &src, const endpoint &dst);
/*!
* \brief Disconnect two endpoints
*/
void disconnect(const endpoint &src, const endpoint &dst);
/*!
* \brief convenience wrapper; used to connect two endpoints
*/
void connect(basic_block_sptr src_block, int src_port,
basic_block_sptr dst_block, int dst_port);
/*!
* \brief convenience wrapper; used to disconnect two endpoints
*/
void disconnect(basic_block_sptr src_block, int src_port,
basic_block_sptr dst_block, int dst_port);
/*!
* \brief Connect two message endpoints
* \details
* Checks the validity of both endpoints, then adds the edge to the
* internal list of edges.
*/
void connect(const msg_endpoint &src, const msg_endpoint &dst);
/*!
* \brief Disconnect two message endpoints
*/
void disconnect(const msg_endpoint &src, const msg_endpoint &dst);
/*!
* \brief Validate flow graph
* \details
* Gathers all used blocks, checks the contiguity of all connected in- and
* outputs, and calls the check_topology method of each block.
*/
void validate();
/*!
* \brief Clear existing flowgraph
*/
void clear();
/*!
* \brief Get vector of edges
*/
const edge_vector_t &edges() const { return d_edges; }
/*!
* \brief Get vector of message edges
*/
const msg_edge_vector_t &msg_edges() const { return d_msg_edges; }
/*!
* \brief calculates all used blocks in a flow graph
* \details
* Iterates over all message edges and stream edges, noting both endpoints in a vector.
*
* \return a unique vector of used blocks
*/
basic_block_vector_t calc_used_blocks();
/*!
* \brief topologically sort blocks
* \details
* Uses depth-first search to return a sorted vector of blocks
*
* \return toplogically sorted vector of blocks. All the sources come first.
*/
basic_block_vector_t topological_sort(basic_block_vector_t &blocks);
/*!
* \brief Calculate vector of disjoint graph partions
* \return vector of disjoint vectors of topologically sorted blocks
*/
std::vector<basic_block_vector_t> partition();
protected:
basic_block_vector_t d_blocks;
edge_vector_t d_edges;
msg_edge_vector_t d_msg_edges;
flowgraph();
std::vector<int> calc_used_ports(basic_block_sptr block, bool check_inputs);
basic_block_vector_t calc_downstream_blocks(basic_block_sptr block, int port);
edge_vector_t calc_upstream_edges(basic_block_sptr block);
bool has_block_p(basic_block_sptr block);
edge calc_upstream_edge(basic_block_sptr block, int port);
private:
void check_valid_port(gr::io_signature::sptr sig, int port);
void check_valid_port(const msg_endpoint &e);
void check_dst_not_used(const endpoint &dst);
void check_type_match(const endpoint &src, const endpoint &dst);
edge_vector_t calc_connections(basic_block_sptr block, bool check_inputs); // false=use outputs
void check_contiguity(basic_block_sptr block, const std::vector<int> &used_ports, bool check_inputs);
basic_block_vector_t calc_downstream_blocks(basic_block_sptr block);
basic_block_vector_t calc_reachable_blocks(basic_block_sptr block, basic_block_vector_t &blocks);
void reachable_dfs_visit(basic_block_sptr block, basic_block_vector_t &blocks);
basic_block_vector_t calc_adjacent_blocks(basic_block_sptr block, basic_block_vector_t &blocks);
basic_block_vector_t sort_sources_first(basic_block_vector_t &blocks);
bool source_p(basic_block_sptr block);
void topological_dfs_visit(basic_block_sptr block, basic_block_vector_t &output);
};
顶层模块top_block类:
/*!
*\brief Top-level hierarchical block representing a flowgraph
* \ingroup container_blk
*/
class GR_RUNTIME_API top_block : public hier_block2
{
private:
friend GR_RUNTIME_API top_block_sptr
make_top_block(const std::string &name);
top_block_impl *d_impl;
protected:
top_block(const std::string &name);
public:
~top_block();
/*!
* \brief The simple interface to running a flowgraph.
*
* Calls start() then wait(). Used to run a flowgraph that will
* stop on its own, or when another thread will call stop().
*
* \param max_noutput_items the maximum number of output items
* allowed for any block in the flowgraph. This passes through to
* the start function; see that function for more details.
*/
void run(int max_noutput_items=100000000);
/*!
* Start the contained flowgraph. Creates one or more threads to
* execute the flow graph. Returns to the caller once the threads
* are created. Calling start() on a top_block that is already
* started IS an error.
*
* \param max_noutput_items the maximum number of output items
* allowed for any block in the flowgraph; the noutput_items can
* always be less than this, but this will cap it as a
* maximum. Use this to adjust the maximum latency a flowgraph can
* exhibit.
*/
void start(int max_noutput_items=100000000);
/*!
* Stop the running flowgraph. Notifies each thread created by the
* scheduler to shutdown, then returns to caller. Calling stop()
* on a top_block that is already stopped IS NOT an error.
*/
void stop();
/*!
* Wait for a flowgraph to complete. Flowgraphs complete when
* either (1) all blocks indicate that they are done (typically
* only when using blocks.file_source, or blocks.head, or (2)
* after stop() has been called to request shutdown. Calling wait
* on a top_block that is not running IS NOT an error (wait
* returns w/o blocking).
*/
void wait();
/*!
* Lock a flowgraph in preparation for reconfiguration. When an
* equal number of calls to lock() and unlock() have occurred, the
* flowgraph will be reconfigured.
*
* N.B. lock() and unlock() may not be called from a flowgraph
* thread (E.g., block::work method) or deadlock will occur
* when reconfiguration happens.
*/
virtual void lock();
/*!
* Unlock a flowgraph in preparation for reconfiguration. When an
* equal number of calls to lock() and unlock() have occurred, the
* flowgraph will be reconfigured.
*
* N.B. lock() and unlock() may not be called from a flowgraph thread
* (E.g., block::work method) or deadlock will occur when
* reconfiguration happens.
*/
virtual void unlock();
/*!
* Returns a string that lists the edge connections in the
* flattened flowgraph.
*/
std::string edge_list();
/*!
* Returns a string that lists the msg edge connections in the
* flattened flowgraph.
*/
std::string msg_edge_list();
/*!
* Displays flattened flowgraph edges and block connectivity
*/
void dump();
//! Get the number of max noutput_items in the flowgraph
int max_noutput_items();
//! Set the maximum number of noutput_items in the flowgraph
void set_max_noutput_items(int nmax);
top_block_sptr to_top_block(); // Needed for Python type coercion
void setup_rpc();
};
可见C++的类中实现了比Python更多的功能,既然这些类都是通过C++编写的,虽然目前还没看到C++流图开发的例子,但理论上来说应该也可以通过C++开发流图。另一方面,由于Python并不能实现真正的多线程,使用C++开发可能也会得到更好地运行效果。