一般的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++开发可能也会得到更好地运行效果。