diff options
213 files changed, 20356 insertions, 7315 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e1e68a05c..625d83def4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,8 +41,8 @@ list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) # Set the version information here set(VERSION_INFO_MAJOR_VERSION 3) set(VERSION_INFO_API_COMPAT 7) -set(VERSION_INFO_MINOR_VERSION 0) -set(VERSION_INFO_MAINT_VERSION 0) +set(VERSION_INFO_MINOR_VERSION 1) +set(VERSION_INFO_MAINT_VERSION git) include(GrVersion) #setup version info # Append -O2 optimization flag for Debug builds @@ -315,8 +315,11 @@ if(NOT CMAKE_MODULES_DIR) set(CMAKE_MODULES_DIR lib/cmake/gnuradio) endif(NOT CMAKE_MODULES_DIR) +# Install all other cmake files into same directory +file(GLOB cmake_others "cmake/Modules/*.cmake") + install( - FILES ${cmake_configs} + FILES ${cmake_configs} ${cmake_others} DESTINATION ${CMAKE_MODULES_DIR} COMPONENT "runtime_devel" ) diff --git a/cmake/Modules/GrPackage.cmake b/cmake/Modules/GrPackage.cmake index d1b0b33205..54a752661d 100644 --- a/cmake/Modules/GrPackage.cmake +++ b/cmake/Modules/GrPackage.cmake @@ -175,3 +175,8 @@ endforeach(filename) set(CPACK_NSIS_MODIFY_PATH ON) set(HLKM_ENV "\\\"SYSTEM\\\\CurrentControlSet\\\\Control\\\\Session Manager\\\\Environment\\\"") + +IF(WIN32) + #Install necessary runtime DLL's + INCLUDE(InstallRequiredSystemLibraries) +ENDIF(WIN32) diff --git a/docs/doxygen/other/thread_affinity.dox b/docs/doxygen/other/thread_affinity.dox index 2f31d9ce53..86634ffdf5 100644 --- a/docs/doxygen/other/thread_affinity.dox +++ b/docs/doxygen/other/thread_affinity.dox @@ -10,13 +10,13 @@ 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(); + 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_t thread, unsigned int n); - void thread_bind_to_processor(gr_thread_t thread, 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_t thread); + 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 @@ -45,21 +45,55 @@ Each block has two new data members: 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 +A block can set and unset its affinity at any time using the following member functions: -- gr_block::set_processor_affinity(const std::vector<unsigned int> &mask) -- gr_block::unset_processor_affinity() +- 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() +- 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. + */ diff --git a/gnuradio-runtime/include/gnuradio/basic_block.h b/gnuradio-runtime/include/gnuradio/basic_block.h index 30ae2ca0f0..4a2d550fb3 100644 --- a/gnuradio-runtime/include/gnuradio/basic_block.h +++ b/gnuradio-runtime/include/gnuradio/basic_block.h @@ -342,6 +342,15 @@ namespace gr { } d_msg_handlers[which_port] = msg_handler_t(msg_handler); } + + virtual void set_processor_affinity(const std::vector<int> &mask) + { throw std::runtime_error("set_processor_affinity not overloaded in child class."); } + + virtual void unset_processor_affinity() + { throw std::runtime_error("unset_processor_affinity not overloaded in child class."); } + + virtual std::vector<int> processor_affinity() + { throw std::runtime_error("processor_affinity not overloaded in child class."); } }; inline bool operator<(basic_block_sptr lhs, basic_block_sptr rhs) diff --git a/gnuradio-runtime/include/gnuradio/block_gateway.h b/gnuradio-runtime/include/gnuradio/block_gateway.h index c2d09de00d..09e372163d 100644 --- a/gnuradio-runtime/include/gnuradio/block_gateway.h +++ b/gnuradio-runtime/include/gnuradio/block_gateway.h @@ -41,6 +41,12 @@ namespace gr { GR_BLOCK_GW_WORK_INTERP, }; + enum tag_propagation_policy_t { + TPP_DONT = 0, + TPP_ALL_TO_ALL = 1, + TPP_ONE_TO_ONE = 2 + }; + /*! * Shared message structure between python and gateway. * Each action type represents a scheduler-called function. diff --git a/gnuradio-runtime/include/gnuradio/fxpt_vco.h b/gnuradio-runtime/include/gnuradio/fxpt_vco.h index 77d58677ce..cbc204c1b8 100644 --- a/gnuradio-runtime/include/gnuradio/fxpt_vco.h +++ b/gnuradio-runtime/include/gnuradio/fxpt_vco.h @@ -61,6 +61,17 @@ namespace gr { *cosx = fxpt::cos(d_phase); } + // compute complex sine a block at a time + void sincos(gr_complex *output, const float *input, int noutput_items, + float k, float ampl = 1.0) + { + for(int i = 0; i < noutput_items; i++) { + output[i] = gr_complex((float)(fxpt::cos(d_phase) * ampl), + (float)(fxpt::sin(d_phase) * ampl)); + adjust_phase(input[i] * k); + } + } + // compute a block at a time void cos(float *output, const float *input, int noutput_items, float k, float ampl = 1.0) { diff --git a/gnuradio-runtime/include/gnuradio/hier_block2.h b/gnuradio-runtime/include/gnuradio/hier_block2.h index ff09f9139d..0d7b138e4d 100644 --- a/gnuradio-runtime/include/gnuradio/hier_block2.h +++ b/gnuradio-runtime/include/gnuradio/hier_block2.h @@ -210,6 +210,29 @@ namespace gr { throw std::invalid_argument("block already has a primitive output port by this name"); hier_message_ports_out = pmt::list_add(hier_message_ports_out, port_id); } + + /*! + * \brief Set the affinity of all blocks in hier_block2 to processor core \p n. + * + * \param mask a vector of ints of the core numbers available to this block. + */ + void set_processor_affinity(const std::vector<int> &mask); + + /*! + * \brief Remove processor affinity for all blocks in hier_block2. + */ + void unset_processor_affinity(); + + /*! + * \brief Get the current processor affinity. + * + * \details This returns the processor affinity value for the first + * block in the hier_block2's list of blocks with the assumption + * that they have always only been set through the hier_block2's + * interface. If any block has been individually set, then this + * call could be misleading. + */ + std::vector<int> processor_affinity(); }; inline hier_block2_sptr cast_to_hier_block2_sptr(basic_block_sptr block) { diff --git a/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h b/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h index 4431d15bd8..92adc0b768 100644 --- a/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h +++ b/gnuradio-runtime/include/gnuradio/rpcregisterhelpers.h @@ -95,6 +95,36 @@ public: // Specialized Extractor Templates template<typename T> +class rpcbasic_extractor<T,char> : public virtual rpcextractor_base<T,char> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(char)) + : rpcextractor_base<T,char>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,char>::_source->*rpcextractor_base<T,char>::_func) + (static_cast<char>(pmt::to_long(msg))); + } +}; + +template<typename T> +class rpcbasic_extractor<T,short> : public virtual rpcextractor_base<T,short> +{ +public: + rpcbasic_extractor(T* source, void (T::*func)(short)) + : rpcextractor_base<T,short>(source, func) + {;} + + void post(pmt::pmt_t which_port, pmt::pmt_t msg) + { + (rpcextractor_base<T,short>::_source->*rpcextractor_base<T,char>::_func) + (static_cast<short>(pmt::to_long(msg))); + } +}; + +template<typename T> class rpcbasic_extractor<T,double> : public virtual rpcextractor_base<T,double> { public: @@ -238,6 +268,50 @@ public: }; template<typename T> +class rpcbasic_inserter<T,std::vector< signed char > > + : public virtual rpcinserter_base<T,std::vector< signed char > > +{ +public: + rpcbasic_inserter(T* source, std::vector< signed char > (T::*func)() const) + : rpcinserter_base<T,std::vector< signed char > >(source, func) + {;} + + rpcbasic_inserter(T* source, std::vector< signed char > (T::*func)()) + : rpcinserter_base<T,std::vector< signed char > >(source, func) + {;} + + pmt::pmt_t retrieve() + { + std::vector< signed char > + vec((rpcinserter_base<T,std::vector< signed char > >:: + _source->*rpcinserter_base<T,std::vector< signed char > >::_func)()); + return pmt::init_s8vector(vec.size(), &vec[0]); + } +}; + +template<typename T> +class rpcbasic_inserter<T,std::vector< short > > + : public virtual rpcinserter_base<T,std::vector< short > > +{ +public: + rpcbasic_inserter(T* source, std::vector< short > (T::*func)() const) + : rpcinserter_base<T,std::vector< short > >(source, func) + {;} + + rpcbasic_inserter(T* source, std::vector< short > (T::*func)()) + : rpcinserter_base<T,std::vector< short > >(source, func) + {;} + + pmt::pmt_t retrieve() + { + std::vector< short > + vec((rpcinserter_base<T,std::vector< short > >:: + _source->*rpcinserter_base<T,std::vector< short > >::_func)()); + return pmt::init_s16vector(vec.size(), &vec[0]); + } +}; + +template<typename T> class rpcbasic_inserter<T,std::vector< int > > : public virtual rpcinserter_base<T,std::vector< int > > { @@ -354,7 +428,7 @@ public: : rpcinserter_base<T,std::complex<double> >(source, func) {;} - pmt::pmt_t retrieve() + pmt::pmt_t retrieve() { std::complex<double > k((rpcinserter_base<T,std::complex<double> >:: _source->*rpcinserter_base<T,std::complex<double> >::_func)()); diff --git a/gnuradio-runtime/lib/controlport/gnuradio.ice b/gnuradio-runtime/lib/controlport/gnuradio.ice index 8318875926..3d6101087a 100644 --- a/gnuradio-runtime/lib/controlport/gnuradio.ice +++ b/gnuradio-runtime/lib/controlport/gnuradio.ice @@ -35,6 +35,7 @@ class Knob {}; class KnobB extends Knob { bool value; }; class KnobC extends Knob { byte value; }; class KnobI extends Knob { int value; }; +class KnobT extends Knob { short value; }; class KnobF extends Knob { float value; }; class KnobD extends Knob { double value; }; class KnobL extends Knob { long value; }; @@ -44,11 +45,12 @@ class KnobZ extends Knob { complex value; }; sequence<bool> VectorB; sequence<byte> VectorC; sequence<int> VectorI; sequence<float> VectorF; sequence<double> VectorD; sequence<string> VectorS; -sequence<long> VectorL; +sequence<long> VectorL; sequence<short> VectorT; class KnobVecB extends Knob { VectorB value; }; class KnobVecC extends Knob { VectorC value; }; class KnobVecI extends Knob { VectorI value; }; +class KnobVecT extends Knob { VectorT value; }; class KnobVecF extends Knob { VectorF value; }; class KnobVecD extends Knob { VectorD value; }; class KnobVecL extends Knob { VectorL value; }; @@ -57,7 +59,7 @@ class KnobVecS extends Knob { VectorS value; }; enum KnobType { KNOBBOOL, KNOBCHAR, KNOBINT, KNOBFLOAT, KNOBDOUBLE, KNOBSTRING, KNOBLONG, KNOBVECBOOL, KNOBVECCHAR, KNOBVECINT, KNOBVECFLOAT, KNOBVECDOUBLE, - KNOBVECSTRING, KNOBVECLONG }; + KNOBVECSTRING, KNOBVECLONG, KNOBSHORT}; const int DISPNULL = 0x0000; const int DISPTIME = 0x0001; diff --git a/gnuradio-runtime/lib/controlport/rpcpmtconverters_ice.cc b/gnuradio-runtime/lib/controlport/rpcpmtconverters_ice.cc index ca6a769f48..efd2a5599c 100644 --- a/gnuradio-runtime/lib/controlport/rpcpmtconverters_ice.cc +++ b/gnuradio-runtime/lib/controlport/rpcpmtconverters_ice.cc @@ -60,6 +60,11 @@ rpcpmtconverter::from_pmt(const pmt::pmt_t& knob, const Ice::Current& c) const int* start((const int*) pmt::s32vector_elements(knob,size)); return new GNURadio::KnobVecI(std::vector<int>(start,start+size)); } + else if (pmt::is_s16vector(knob)) { + size_t size(pmt::length(knob)); + const short* start((const short*) pmt::s16vector_elements(knob,size)); + return new GNURadio::KnobVecT(std::vector<short>(start,start+size)); + } else if(pmt::is_f32vector(knob)) { size_t size(pmt::length(knob)); const float* start((const float*) pmt::f32vector_elements(knob,size)); @@ -70,6 +75,11 @@ rpcpmtconverter::from_pmt(const pmt::pmt_t& knob, const Ice::Current& c) const uint8_t* start((const uint8_t*) pmt::u8vector_elements(knob,size)); return new GNURadio::KnobVecC(std::vector<Ice::Byte>(start,start+size)); } + else if (pmt::is_s8vector(knob)) { + size_t size(pmt::length(knob)); + const int8_t* start((const int8_t*) pmt::s8vector_elements(knob,size)); + return new GNURadio::KnobVecC(std::vector<Ice::Byte>(start,start+size)); + } else { std::cerr << "Error: Don't know how to handle Knob Type (from): " << std::endl; assert(0);} //TODO: VECTORS!!! @@ -92,6 +102,10 @@ rpcpmtconverter::to_pmt(const GNURadio::KnobPtr& knob, const Ice::Current& c) GNURadio::KnobIPtr k(GNURadio::KnobIPtr::dynamicCast(knob)); return pmt::mp(k->value); } + else if(id == "KnobT") { + GNURadio::KnobTPtr k(GNURadio::KnobTPtr::dynamicCast(knob)); + return pmt::mp(k->value); + } else if(id == "KnobS") { GNURadio::KnobSPtr k(GNURadio::KnobSPtr::dynamicCast(knob)); return pmt::string_to_symbol(k->value); diff --git a/gnuradio-runtime/lib/hier_block2.cc b/gnuradio-runtime/lib/hier_block2.cc index f26da18e54..e0acba30a0 100644 --- a/gnuradio-runtime/lib/hier_block2.cc +++ b/gnuradio-runtime/lib/hier_block2.cc @@ -157,4 +157,22 @@ namespace gr { return new_ffg; } + void + hier_block2::set_processor_affinity(const std::vector<int> &mask) + { + d_detail->set_processor_affinity(mask); + } + + void + hier_block2::unset_processor_affinity() + { + d_detail->unset_processor_affinity(); + } + + std::vector<int> + hier_block2::processor_affinity() + { + return d_detail->processor_affinity(); + } + } /* namespace gr */ diff --git a/gnuradio-runtime/lib/hier_block2_detail.cc b/gnuradio-runtime/lib/hier_block2_detail.cc index 704e92599e..ebfaa6fa69 100644 --- a/gnuradio-runtime/lib/hier_block2_detail.cc +++ b/gnuradio-runtime/lib/hier_block2_detail.cc @@ -657,4 +657,29 @@ namespace gr { d_owner->unlock(); } + void + hier_block2_detail::set_processor_affinity(const std::vector<int> &mask) + { + basic_block_vector_t tmp = d_fg->calc_used_blocks(); + for(basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) { + (*p)->set_processor_affinity(mask); + } + } + + void + hier_block2_detail::unset_processor_affinity() + { + basic_block_vector_t tmp = d_fg->calc_used_blocks(); + for(basic_block_viter_t p = tmp.begin(); p != tmp.end(); p++) { + (*p)->unset_processor_affinity(); + } + } + + std::vector<int> + hier_block2_detail::processor_affinity() + { + basic_block_vector_t tmp = d_fg->calc_used_blocks(); + return tmp[0]->processor_affinity(); + } + } /* namespace gr */ diff --git a/gnuradio-runtime/lib/hier_block2_detail.h b/gnuradio-runtime/lib/hier_block2_detail.h index 99bf6e8ef1..806738360f 100644 --- a/gnuradio-runtime/lib/hier_block2_detail.h +++ b/gnuradio-runtime/lib/hier_block2_detail.h @@ -54,6 +54,10 @@ namespace gr { void unlock(); void flatten_aux(flat_flowgraph_sptr sfg) const; + void set_processor_affinity(const std::vector<int> &mask); + void unset_processor_affinity(); + std::vector<int> processor_affinity(); + private: // Private implementation data hier_block2 *d_owner; diff --git a/gnuradio-runtime/lib/math/qa_fxpt_vco.cc b/gnuradio-runtime/lib/math/qa_fxpt_vco.cc index ee9865e926..8ee402fdc1 100644 --- a/gnuradio-runtime/lib/math/qa_fxpt_vco.cc +++ b/gnuradio-runtime/lib/math/qa_fxpt_vco.cc @@ -102,6 +102,26 @@ qa_fxpt_vco::t1() void qa_fxpt_vco::t2() { + gr::vco<gr_complex,float> ref_vco; + gr::fxpt_vco new_vco; + gr_complex ref_block[SIN_COS_BLOCK_SIZE]; + gr_complex new_block[SIN_COS_BLOCK_SIZE]; + float input[SIN_COS_BLOCK_SIZE]; + double max_error = 0; + + for(int i = 0; i < SIN_COS_BLOCK_SIZE; i++) { + input[i] = sin(double(i)); + } + + ref_vco.sincos(ref_block, input, SIN_COS_BLOCK_SIZE, SIN_COS_K, SIN_COS_AMPL); + new_vco.sincos(new_block, input, SIN_COS_BLOCK_SIZE, SIN_COS_K, SIN_COS_AMPL); + + for(int i = 0; i < SIN_COS_BLOCK_SIZE; i++) { + CPPUNIT_ASSERT_COMPLEXES_EQUAL(ref_block[i], new_block[i], SIN_COS_TOLERANCE); + max_error = max_d(max_error, abs(ref_block[i]-new_block[i])); + } + CPPUNIT_ASSERT_DOUBLES_EQUAL(ref_vco.get_phase(), new_vco.get_phase(), SIN_COS_TOLERANCE); + // printf ("Fxpt max error %.9f, max phase error %.9f\n", max_error, ref_vco.get_phase()-new_vco.get_phase()); } void diff --git a/gnuradio-runtime/lib/math/vco.h b/gnuradio-runtime/lib/math/vco.h index fa11732c1f..d8a6fbb415 100644 --- a/gnuradio-runtime/lib/math/vco.h +++ b/gnuradio-runtime/lib/math/vco.h @@ -64,6 +64,9 @@ namespace gr { // compute sin and cos for current phase angle void sincos(float *sinx, float *cosx) const; + void sincos(gr_complex *output, const float *input, + int noutput_items, double k, double ampl = 1.0); + // compute cos or sin for current phase angle float cos() const { return std::cos(d_phase); } float sin() const { return std::sin(d_phase); } @@ -85,6 +88,17 @@ namespace gr { template<class o_type, class i_type> void + vco<o_type,i_type>::sincos(gr_complex *output, const float *input, + int noutput_items, double k, double ampl) + { + for(int i = 0; i < noutput_items; i++) { + output[i] = gr_complex(cos() * ampl, sin() * ampl); + adjust_phase(input[i] * k); + } + } + + template<class o_type, class i_type> + void vco<o_type,i_type>::cos(float *output, const float *input, int noutput_items, double k, double ampl) { diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py index 4e9ef13133..78b83c2411 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/GrDataPlotter.py @@ -24,7 +24,7 @@ from gnuradio import gr from gnuradio import blocks from gnuradio import filter from gnuradio.ctrlport import GNURadio -import sys, time +import sys, time, struct try: from gnuradio import qtgui @@ -420,14 +420,72 @@ class GrDataPlotterValueTable: self.treeWidget.resizeColumnToContents(0) def updateItems(self, knobs, knobprops): - items = []; - self.treeWidget.clear() - for k, v in knobs.iteritems(): - val = v.value - if(type(val) == GNURadio.complex): - val = val.re + val.im*1j - - items.append(QtGui.QTreeWidgetItem([str(k), str(val), - knobprops[k].units, - knobprops[k].description])) - self.treeWidget.insertTopLevelItems(0, items) + items = [] + foundKeys = [] + deleteKeys = [] + numItems = self.treeWidget.topLevelItemCount() + + # The input knobs variable is a dict of stats to display in the tree. + + # Update tree stat values with new values found in knobs. + # Track found keys and track keys in tree that are not in input knobs. + for i in range(0, numItems): + item = self.treeWidget.topLevelItem(i) + + # itemKey is the text in the first column of a QTreeWidgetItem + itemKey = str(item.text(0)) + if itemKey in knobs.keys(): + + # This key was found in the tree, update its values. + foundKeys.append(itemKey) + v = knobs[itemKey].value + units = str(knobprops[itemKey].units) + descr = str(knobprops[itemKey].description) + + if(type(v) == GNURadio.complex): + v = v.re + v.im*1j + # If it's a byte stream, Python thinks it's a string. + # Unpack and convert to floats for plotting. + # Ignore the edge list knob if it's being exported + elif(type(v) == str and itemKey.find('probe2_b') == 0): + v = struct.unpack(len(v)*'b', v) + + # Convert the final value to a string for displaying + v = str(v) + + if (item.text(1) != v or + item.text(2) != units or + item.text(3) != descr): + + item.setText(1, v) + item.setText(2, units) + item.setText(3, descr) + else: + # This item is not in the knobs list...track it for removal. + deleteKeys.append(itemKey) + + # Add items to tree that are not currently in the tree. + for k in knobs.keys(): + if k not in foundKeys: + v = knobs[k].value + if(type(v) == GNURadio.complex): + v = v.re + v.im*1j + # If it's a byte stream, Python thinks it's a string. + # Unpack and convert to floats for plotting. + # Ignore the edge list knob if it's being exported + elif(type(v) == str and k.find('probe2_b') == 0): + v = struct.unpack(len(v)*'b', v) + + item = QtGui.QTreeWidgetItem([k, str(v), + knobprops[k].units, knobprops[k].description]) + self.treeWidget.addTopLevelItem(item) + + # Remove items currently in tree that are not in the knob list. + for itemKey in deleteKeys: + qtwiList = self.treeWidget.findItems(itemKey, Qt.Qt.MatchFixedString) + if (len(qtwiList) > 1): + raise Exception('More than one item with key %s in tree' % + itemKey) + elif (len(qtwiList) == 1): + i = self.treeWidget.indexOfTopLevelItem(qtwiList[0]) + self.treeWidget.takeTopLevelItem(i) diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor index ac6e6a4675..b087ad5c71 100755 --- a/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor +++ b/gnuradio-runtime/python/gnuradio/ctrlport/gr-ctrlport-monitor @@ -24,7 +24,7 @@ from gnuradio import gr, ctrlport from PyQt4 import QtCore,Qt import PyQt4.QtGui as QtGui -import os, sys, time +import os, sys, time, struct import Ice from gnuradio.ctrlport.IceRadioClient import * @@ -109,23 +109,8 @@ class MAINWindow(QtGui.QMainWindow): title = "{0}:{1}".format(r[3], r[5]) props = radio.properties([key]) - pmin = props[key].min.value - pmax = props[key].max.value - # Convert from GNURadio::complex to Python complex - if(type(pmin) == GNURadio.complex): - pmin = pmin.re + pmin.im*1j - if(type(pmax) == GNURadio.complex): - pmax = pmax.re + pmax.im*1j - - if pmin == []: - pmin = None - else: - pmin = 1.1*abs(pmin) - if pmax == []: - pmax = None - else: - pmax = 1.1*abs(pmax) + pmin,pmax = get_minmax(props[key]) # Use display option mask of item to set up available plot # types and default options. @@ -197,22 +182,7 @@ class MAINWindow(QtGui.QMainWindow): r = str(tree.radio).split(" ") title = "{0}:{1}".format(r[3], r[5]) - pmin = knobprop.min.value - pmax = knobprop.max.value - - if(type(pmin) == GNURadio.complex): - pmin = pmin.re + pmin.im*1j - if(type(pmax) == GNURadio.complex): - pmax = pmax.re + pmax.im*1j - - if pmin == []: - pmin = None - else: - pmin = 1.1*pmin - if pmax == []: - pmax = None - else: - pmax = 1.1*pmax + pmin,pmax = get_minmax(knobprop) disp = knobprop.display if(disp & gr.DISPTIME): @@ -337,6 +307,13 @@ class MAINWindow(QtGui.QMainWindow): d = knobs[n].value if(type(d) == GNURadio.complex): d = [d.re, d.im] + + # If it's a byte stream, Python thinks it's a string. + # Unpack and convert to floats for plotting. + if(type(d) == str and n.find('probe2_b') == 0): + d = struct.unpack(len(d)*'b', d) + d = [float(di) for di in d] + data.append(d) plot.update(data) plot.stop() @@ -352,10 +329,6 @@ class MAINWindow(QtGui.QMainWindow): self.newConAct = QtGui.QAction("&New Connection", self, shortcut=QtGui.QKeySequence.New, statusTip="Create a new file", triggered=self.newCon) - #self.newAct = QtGui.QAction(QtGui.QIcon(':/images/new.png'), "&New Plot", - self.newPlotAct = QtGui.QAction("&New Plot", - self, - statusTip="Create a new file", triggered=self.newPlotF) self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q", statusTip="Exit the application", @@ -408,7 +381,6 @@ class MAINWindow(QtGui.QMainWindow): def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newConAct) - self.fileMenu.addAction(self.newPlotAct) self.fileMenu.addAction(self.urAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.exitAct) @@ -435,7 +407,6 @@ class MAINWindow(QtGui.QMainWindow): def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.newConAct) - self.fileToolBar.addAction(self.newPlotAct) self.fileToolBar.addAction(self.urAct) self.fileToolBar = self.addToolBar("Window") @@ -744,6 +715,33 @@ class MForm(QtGui.QWidget): self.parent.propertiesMenu(itemname, self.radio, self.uid) +def get_minmax(p): + pmin = p.min.value + pmax = p.max.value + + # Find min/max or real or imag for GNURadio::complex + if(type(pmin) == GNURadio.complex): + pmin = min(pmin.re, pmin.im) + if(type(pmax) == GNURadio.complex): + pmax = max(pmax.re, pmax.im) + + # If it's a byte stream, Python thinks it's a string. + if(type(pmin) == str): + pmin = struct.unpack('b', pmin)[0] + if(type(pmax) == str): + pmax = struct.unpack('b', pmax)[0] + + if pmin == []: + pmin = None + else: + pmin = 1.1*float(pmin) + if pmax == []: + pmax = None + else: + pmax = 1.1*float(pmax) + + return pmin, pmax + class MyClient(IceRadioClient): def __init__(self): IceRadioClient.__init__(self, MAINWindow) diff --git a/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py b/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py index e067b9b0a6..8bb26d93a1 100644 --- a/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py +++ b/gnuradio-runtime/python/gnuradio/ctrlport/monitor.py @@ -33,7 +33,7 @@ class monitor: try: # setup export prefs gr.prefs().singleton().set_bool("ControlPort","on",True); - if(tool == "gr-ctrlport-monitor"): + if(tool == "gr-perf-monitorx"): gr.prefs().singleton().set_bool("ControlPort","edges_list",True); gr.prefs().singleton().set_bool("PerfCounters","on",True); gr.prefs().singleton().set_bool("PerfCounters","export",True); diff --git a/gnuradio-runtime/swig/hier_block2.i b/gnuradio-runtime/swig/hier_block2.i index b455e02a7e..87c936544a 100644 --- a/gnuradio-runtime/swig/hier_block2.i +++ b/gnuradio-runtime/swig/hier_block2.i @@ -87,6 +87,10 @@ namespace gr { void message_port_register_hier_in(pmt::pmt_t port_id); void message_port_register_hier_out(pmt::pmt_t port_id); + void set_processor_affinity(const std::vector<int> &mask); + void unset_processor_affinity(); + std::vector<int> processor_affinity(); + gr::hier_block2_sptr to_hier_block2(); // Needed for Python type coercion }; } diff --git a/gr-atsc/include/gnuradio/atsc/field_sync_mux.h b/gr-atsc/include/gnuradio/atsc/field_sync_mux.h index dbf08c5132..2cccb52aec 100644 --- a/gr-atsc/include/gnuradio/atsc/field_sync_mux.h +++ b/gr-atsc/include/gnuradio/atsc/field_sync_mux.h @@ -38,7 +38,7 @@ ATSC_API atsc_field_sync_mux_sptr atsc_make_field_sync_mux(); * * input: atsc_data_segment; output: atsc_data_segment */ -class ATSC_API atsc_field_sync_mux : public gr::sync_block +class ATSC_API atsc_field_sync_mux : public gr::block { friend ATSC_API atsc_field_sync_mux_sptr atsc_make_field_sync_mux(); @@ -46,17 +46,20 @@ class ATSC_API atsc_field_sync_mux : public gr::sync_block public: void forecast (int noutput_items, gr_vector_int &ninput_items_required); - int work (int noutput_items, + int general_work (int noutput_items, + gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items); static const int N_SAVED_SYMBOLS = 12; - void reset() { /* nop */ } + void reset() + { + d_already_output_field_sync = false; + } protected: - gr_uint64 d_current_index; bool d_already_output_field_sync; unsigned char d_saved_symbols[N_SAVED_SYMBOLS]; }; diff --git a/gr-atsc/lib/atsc_field_sync_mux.cc b/gr-atsc/lib/atsc_field_sync_mux.cc index 5520a74877..6b0586dbfc 100644 --- a/gr-atsc/lib/atsc_field_sync_mux.cc +++ b/gr-atsc/lib/atsc_field_sync_mux.cc @@ -37,7 +37,7 @@ atsc_make_field_sync_mux() } atsc_field_sync_mux::atsc_field_sync_mux() - : gr::sync_block("atsc_field_sync_mux", + : gr::block("atsc_field_sync_mux", gr::io_signature::make(1, 1, sizeof(atsc_data_segment)), gr::io_signature::make(1, 1, sizeof(atsc_data_segment))) { @@ -148,21 +148,24 @@ atsc_field_sync_mux::forecast (int noutput_items, gr_vector_int &ninput_items_re { unsigned ninputs = ninput_items_required.size(); for (unsigned i = 0; i < ninputs; i++) - ninput_items_required[i] = fixed_rate_noutput_to_ninput (noutput_items); + ninput_items_required[i] = noutput_items; } int -atsc_field_sync_mux::work (int noutput_items, +atsc_field_sync_mux::general_work (int noutput_items, + gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { + int in_length = ninput_items[0]; const atsc_data_segment *in = (const atsc_data_segment *) input_items[0]; atsc_data_segment *out = (atsc_data_segment *) output_items[0]; - unsigned int index = 0; - for (int outdex = 0; outdex < noutput_items; outdex++){ + int index = 0; + int outdex = 0; + for (outdex = 0; outdex < noutput_items && index < in_length; outdex++){ assert (in[index].pli.regular_seg_p ()); @@ -197,7 +200,6 @@ atsc_field_sync_mux::work (int noutput_items, } } - d_current_index += index; - - return noutput_items; + this->consume_each(index); + return outdex; } diff --git a/gr-atsc/python/atsc/CMakeLists.txt b/gr-atsc/python/atsc/CMakeLists.txt index 881a3bb9fc..ab19b13e2d 100644 --- a/gr-atsc/python/atsc/CMakeLists.txt +++ b/gr-atsc/python/atsc/CMakeLists.txt @@ -32,11 +32,7 @@ GR_PYTHON_INSTALL( GR_PYTHON_INSTALL( PROGRAMS - btl-fsd.py - fpll.py - interp.py - xlate.py - viterbi-out.py + atsc_rx.py DESTINATION ${GR_PKG_DATA_DIR}/examples/atsc COMPONENT "atsc_examples" ) diff --git a/gr-atsc/python/atsc/README b/gr-atsc/python/atsc/README index 74d6ba134c..38772c2f10 100644 --- a/gr-atsc/python/atsc/README +++ b/gr-atsc/python/atsc/README @@ -1,11 +1,11 @@ -Decoding ATSC using 19.2MSps rate over 5 processes --------------------------------------------------- +Decoding ATSC using 6.4MSps rate +--------------------------------- 1) Verify signal, adjust antenna and find best gain setting using uhd_fft.py, station frequency from the fcc video database, and sample rate to 6.4e6. 2) Capture data - adjust gain (-g) frequency (-f) and which side -the tvrx is on to fit your local setup: +the daughterboard is on to fit your local setup: uhd_rx_cfile.py -s --samp-rate=6.4e6 -g 65 -f 503e6 atsc_data_6-4m_complex @@ -14,21 +14,6 @@ striped sata drives. Make sure there are no or very few Ou overruns. Saving the raw usrp data in 'short' form halves the disk space/bus bandwidth that the usual complex form uses. -3) Make pipes: - -mkfifo /tmp/atsc_pipe_1 -mkfifo /tmp/atsc_pipe_2 -mkfifo /tmp/atsc_pipe_3 -mkfifo /tmp/atsc_pipe_4 -mkfifo /tmp/atsc_pipe_5 - -4) In seperate windows run processes: - -./interp_short.py <input rf data at 6.4Msps> -./xlate.py -./fpll.py -./btl-fsd.py -./viterbi-out.py <output mpeg transport stream> - - +3) Run atsc_rx.py: +./atsc_rx.py atsc_data_6-4m_complex outfile.ts
\ No newline at end of file diff --git a/gr-atsc/python/atsc/all_atsc.py b/gr-atsc/python/atsc/all_atsc.py deleted file mode 100644 index 7cac785149..0000000000 --- a/gr-atsc/python/atsc/all_atsc.py +++ /dev/null @@ -1,142 +0,0 @@ -#!/usr/bin/env /usr/bin/python -# -# Copyright 2004 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# -# This module starts the atsc processing chain taking the captured -# off-air signal created with: -# -# uhd_rx_cfile.py --samp-rate=6.4e6 -# -f <center of tv signal channel freq> -# -g <appropriate gain for best signal / noise> -# -s output shorts -# -# All this module does is multiply the sample rate by 3, from 6.4e6 to -# 19.2e6 complex samples / sec, then lowpass filter with a cutoff of 3.2MHz -# and a transition band width of .5MHz. Center of the tv channels is -# then at 0 with edges at -3.2MHz and 3.2MHz. - -from gnuradio import gr, atsc -from gnuradio import filter -from gnuradio import blocks -import sys, os, math - -def graph (args): - - nargs = len(args) - if nargs == 2: - infile = args[0] - outfile = args[1] - else: - raise ValueError('usage: interp.py input_file output_file\n') - - tb = gr.top_block () - - # Convert to a from shorts to a stream of complex numbers. - srcf = blocks.file_source (gr.sizeof_short,infile) - s2ss = blocks.stream_to_streams(gr.sizeof_short,2) - s2f1 = blocks.short_to_float() - s2f2 = blocks.short_to_float() - src0 = blocks.float_to_complex() - tb.connect(srcf, s2ss) - tb.connect((s2ss, 0), s2f1, (src0, 0)) - tb.connect((s2ss, 1), s2f2, (src0, 1)) - - # Low pass filter it and increase sample rate by a factor of 3. - lp_coeffs = filter.firdes.low_pass ( 3, 19.2e6, 3.2e6, .5e6, filter.firdes.WIN_HAMMING ) - lp = filter.interp_fir_filter_ccf ( 3, lp_coeffs ) - tb.connect(src0, lp) - - # Upconvert it. - duc_coeffs = filter.firdes.low_pass ( 1, 19.2e6, 9e6, 1e6, filter.firdes.WIN_HAMMING ) - duc = filter.freq_xlating_fir_filter_ccf ( 1, duc_coeffs, 5.75e6, 19.2e6 ) - # Discard the imaginary component. - c2f = blocks.complex_to_float() - tb.connect(lp, duc, c2f) - - # Frequency Phase Lock Loop - input_rate = 19.2e6 - IF_freq = 5.75e6 - # 1/2 as wide because we're designing lp filter - symbol_rate = atsc.ATSC_SYMBOL_RATE/2. - NTAPS = 279 - tt = filter.firdes.root_raised_cosine (1.0, input_rate, symbol_rate, .115, NTAPS) - # heterodyne the low pass coefficients up to the specified bandpass - # center frequency. Note that when we do this, the filter bandwidth - # is effectively twice the low pass (2.69 * 2 = 5.38) and hence - # matches the diagram in the ATSC spec. - arg = 2. * math.pi * IF_freq / input_rate - t=[] - for i in range(len(tt)): - t += [tt[i] * 2. * math.cos(arg * i)] - rrc = filter.fir_filter_fff(1, t) - - fpll = atsc.fpll() - - pilot_freq = IF_freq - 3e6 + 0.31e6 - lower_edge = 6e6 - 0.31e6 - upper_edge = IF_freq - 3e6 + pilot_freq - transition_width = upper_edge - lower_edge - lp_coeffs = filter.firdes.low_pass(1.0, - input_rate, - (lower_edge + upper_edge) * 0.5, - transition_width, - filter.firdes.WIN_HAMMING); - - lp_filter = filter.fir_filter_fff(1,lp_coeffs) - - alpha = 1e-5 - iir = filter.single_pole_iir_filter_ff(alpha) - remove_dc = blocks.sub_ff() - - tb.connect(c2f, fpll, lp_filter) - tb.connect(lp_filter, iir) - tb.connect(lp_filter, (remove_dc,0)) - tb.connect(iir, (remove_dc,1)) - - # Bit Timing Loop, Field Sync Checker and Equalizer - - btl = atsc.bit_timing_loop() - fsc = atsc.fs_checker() - eq = atsc.equalizer() - fsd = atsc.field_sync_demux() - - tb.connect(remove_dc, btl) - tb.connect((btl, 0),(fsc, 0),(eq, 0),(fsd, 0)) - tb.connect((btl, 1),(fsc, 1),(eq, 1),(fsd, 1)) - - # Viterbi - - viterbi = atsc.viterbi_decoder() - deinter = atsc.deinterleaver() - rs_dec = atsc.rs_decoder() - derand = atsc.derandomizer() - depad = atsc.depad() - dst = blocks.file_sink(gr.sizeof_char, outfile) - tb.connect(fsd, viterbi, deinter, rs_dec, derand, depad, dst) - - dst2 = blocks.file_sink(gr.sizeof_gr_complex, "atsc_complex.data") - tb.connect(src0, dst2) - - tb.run () - -if __name__ == '__main__': - graph (sys.argv[1:]) - - diff --git a/gr-atsc/python/atsc/atsc_rx.py b/gr-atsc/python/atsc/atsc_rx.py new file mode 100755 index 0000000000..377c4daa29 --- /dev/null +++ b/gr-atsc/python/atsc/atsc_rx.py @@ -0,0 +1,134 @@ +#!/usr/bin/env /usr/bin/python +# +# Copyright 2004, 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# This module starts the atsc processing chain taking the captured +# off-air signal created with: +# +# uhd_rx_cfile.py --samp-rate=6.4e6 +# -f <center of tv signal channel freq> +# -g <appropriate gain for best signal / noise> +# -s output shorts +# +# This python script converts from interleaved shorts to the complex data type, +# then multiplies the sample rate by 3, from 6.4e6 to 19.2e6 +# complex samples / sec, then lowpass filters with a cutoff of 3.2MHz +# and a transition band width of .5MHz. Center of the tv channels is +# now at 0 with edges at -3.2MHz and 3.2MHz. This puts the pilot at +# -3MHz + 309KHz. Next a root raised cosine filter is aplied to match the one +# in the transmitter and thus reduce ISI. The phased locked loop then locks to +# the pilot and outputs just the real part of the signal ( as information is +# not stored in the phase with atsc ), this is then feed to the bit lock +# loop, this looks for the bit sync marker put at the beginning of every segment +# field, this then adjusts the timing so the amplitude will be sampled at the +# correct sample ( sub-sample is used in this case ). +# +# Output is float. + +from gnuradio import gr, analog, atsc +from gnuradio import blocks +from gnuradio import filter +import sys, math, os + +def graph (args): + + print os.getpid() + + nargs = len(args) + if nargs == 2: + infile = args[0] + outfile = args[1] + else: + raise ValueError('usage: interp.py input_file output_file.ts\n') + + input_rate = 19.2e6 + IF_freq = 5.75e6 + + tb = gr.top_block() + + # Read from input file + srcf = blocks.file_source(gr.sizeof_short, infile) + + # Convert interleaved shorts (I,Q,I,Q) to complex + is2c = blocks.interleaved_short_to_complex() + + # 1/2 as wide because we're designing lp filter + symbol_rate = atsc.ATSC_SYMBOL_RATE/2. + NTAPS = 279 + tt = filter.firdes.root_raised_cosine (1.0, input_rate / 3, symbol_rate, .1152, NTAPS) + rrc = filter.fir_filter_ccf(1, tt) + + # Interpolate Filter our 6MHz wide signal centered at 0 + ilp_coeffs = filter.firdes.low_pass(1, input_rate, 3.2e6, .5e6, filter.firdes.WIN_HAMMING) + ilp = filter.interp_fir_filter_ccf(3, ilp_coeffs) + + # Move the center frequency to 5.75MHz ( this wont be needed soon ) + duc_coeffs = filter.firdes.low_pass ( 1, 19.2e6, 9e6, 1e6, filter.firdes.WIN_HAMMING ) + duc = filter.freq_xlating_fir_filter_ccf ( 1, duc_coeffs, -5.75e6, 19.2e6 ) + + # fpll input is float + c2f = blocks.complex_to_float() + + # Phase locked loop + fpll = atsc.fpll() + + # Clean fpll output + lp_coeffs2 = filter.firdes.low_pass (1.0, + input_rate, + 5.75e6, + 120e3, + filter.firdes.WIN_HAMMING); + lp_filter = filter.fir_filter_fff (1, lp_coeffs2) + + # Remove pilot ( at DC now ) + iir = filter.single_pole_iir_filter_ff(1e-5) + remove_dc = blocks.sub_ff() + + # Bit Timing Loop, Field Sync Checker and Equalizer + btl = atsc.bit_timing_loop() + fsc = atsc.fs_checker() + eq = atsc.equalizer() + fsd = atsc.field_sync_demux() + + # Viterbi + viterbi = atsc.viterbi_decoder() + deinter = atsc.deinterleaver() + rs_dec = atsc.rs_decoder() + derand = atsc.derandomizer() + depad = atsc.depad() + + # Write to output file + outf = blocks.file_sink(gr.sizeof_char,outfile) + + # Connect it all together + tb.connect( srcf, is2c, rrc, ilp, duc, c2f, fpll, lp_filter) + tb.connect( lp_filter, iir ) + tb.connect( lp_filter, (remove_dc, 0) ) + tb.connect( iir, (remove_dc, 1) ) + tb.connect( remove_dc, btl ) + tb.connect( (btl, 0), (fsc, 0), (eq, 0), (fsd,0) ) + tb.connect( (btl, 1), (fsc, 1), (eq, 1), (fsd,1) ) + tb.connect( fsd, viterbi, deinter, rs_dec, derand, depad, outf ) + + tb.run() + +if __name__ == '__main__': + graph (sys.argv[1:]) + diff --git a/gr-atsc/python/atsc/btl-fsd.py b/gr-atsc/python/atsc/btl-fsd.py deleted file mode 100755 index 6bcab3dce3..0000000000 --- a/gr-atsc/python/atsc/btl-fsd.py +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2005,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# - -from gnuradio import gr -from gnuradio import atsc -from gnuradio import blocks -import os - -print os.getpid() - -tb = gr.top_block() - -btl = atsc.bit_timing_loop() -fsc = atsc.fs_checker() -eq = atsc.equalizer() -fsd = atsc.field_sync_demux() - -out_data = blocks.file_sink(atsc.sizeof_atsc_soft_data_segment,"/tmp/atsc_pipe_5") - -inp = blocks.file_source(gr.sizeof_float,"/tmp/atsc_pipe_3") - -tb.connect(inp,btl) -tb.connect((btl,0),(fsc,0),(eq,0),(fsd,0)) -tb.connect((btl,1),(fsc,1),(eq,1),(fsd,1)) -tb.connect(fsd,out_data) - -tb.run() - - diff --git a/gr-atsc/python/atsc/fpll.py b/gr-atsc/python/atsc/fpll.py deleted file mode 100755 index dee81da13d..0000000000 --- a/gr-atsc/python/atsc/fpll.py +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2005 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# - -from gnuradio import gr, atsc -from gnuradio import blocks -from gnuradio import filter -import math, os - -def main(): - - print os.getpid() - - tb = gr.top_block() - - u = blocks.file_source(gr.sizeof_float,"/tmp/atsc_pipe_2") - - input_rate = 19.2e6 - IF_freq = 5.75e6 - - - # 1/2 as wide because we're designing lp filter - symbol_rate = atsc.ATSC_SYMBOL_RATE/2. - NTAPS = 279 - tt = filter.firdes.root_raised_cosine (1.0, input_rate, symbol_rate, .115, NTAPS) - # heterodyne the low pass coefficients up to the specified bandpass - # center frequency. Note that when we do this, the filter bandwidth - # is effectively twice the low pass (2.69 * 2 = 5.38) and hence - # matches the diagram in the ATSC spec. - arg = 2. * math.pi * IF_freq / input_rate - t=[] - for i in range(len(tt)): - t += [tt[i] * 2. * math.cos(arg * i)] - rrc = filter.fir_filter_fff(1, t) - - fpll = atsc.fpll() - - pilot_freq = IF_freq - 3e6 + 0.31e6 - lower_edge = 6e6 - 0.31e6 - upper_edge = IF_freq - 3e6 + pilot_freq - transition_width = upper_edge - lower_edge - lp_coeffs = filter.firdes.low_pass (1.0, - input_rate, - (lower_edge + upper_edge) * 0.5, - transition_width, - filter.firdes.WIN_HAMMING); - - lp_filter = filter.fir_filter_fff (1,lp_coeffs) - - alpha = 1e-5 - iir = filter.single_pole_iir_filter_ff(alpha) - remove_dc = blocks.sub_ff() - - out = blocks.file_sink(gr.sizeof_float,"/tmp/atsc_pipe_3") - # out = blocks.file_sink(gr.sizeof_float,"/mnt/sata/atsc_data_float") - - tb.connect(u, fpll, lp_filter) - tb.connect(lp_filter, iir) - tb.connect(lp_filter, (remove_dc,0)) - tb.connect(iir, (remove_dc,1)) - tb.connect(remove_dc, out) - - tb.run() - - -if __name__ == '__main__': - main () - - - diff --git a/gr-atsc/python/atsc/interp.py b/gr-atsc/python/atsc/interp.py deleted file mode 100755 index ee2d234892..0000000000 --- a/gr-atsc/python/atsc/interp.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env /usr/bin/python -# -# Copyright 2004,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# -# This module starts the atsc processing chain taking the captured -# off-air signal created with: -# -# uhd_rx_cfile.py --samp-rate=6.4e6 -# -f <center of tv signal channel freq> -# -g <appropriate gain for best signal / noise> -# -# All this module does is multiply the sample rate by 3, from 6.4e6 to -# 19.2e6 complex samples / sec, then lowpass filter with a cutoff of 3.2MHz -# and a transition band width of .5MHz. Center of the tv channels is -# then at 0 with edges at -3.2MHz and 3.2MHz. - -from gnuradio import gr -from gnuradio import blocks -import sys - -def graph(args): - - nargs = len(args) - if nargs == 1: - infile = args[0] - else: - sys.stderr.write('usage: interp.py input_file\n') - sys.exit(1) - - tb = gr.top_block() - - src0 = blocks.file_source(gr.sizeof_gr_complex, infile) - - lp_coeffs = filter.firdes.low_pass(3, 19.2e6, 3.2e6, .5e6, - filter.firdes.WIN_HAMMING ) - lp = filter.interp_fir_filter_ccf(1, lp_coeffs) - - file = blocks.file_sink(gr.sizeof_gr_complex, "/tmp/atsc_pipe_1") - - tb.connect(src0, lp, file) - - tb.start() - raw_input('Head End: Press Enter to stop') - tb.stop() - -if __name__ == '__main__': - graph(sys.argv[1:]) - - diff --git a/gr-atsc/python/atsc/interp_short.py b/gr-atsc/python/atsc/interp_short.py deleted file mode 100755 index d07b941c12..0000000000 --- a/gr-atsc/python/atsc/interp_short.py +++ /dev/null @@ -1,78 +0,0 @@ -#!/usr/bin/env /usr/bin/python -# -# Copyright 2004 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# -# This module starts the atsc processing chain taking the captured -# off-air signal created with: -# -# uhd_rx_cfile.py --samp-rate=6.4e6 -# -f <center of tv signal channel freq> -# -g <appropriate gain for best signal / noise> -# -s output shorts -# -# All this module does is multiply the sample rate by 3, from 6.4e6 to -# 19.2e6 complex samples / sec, then lowpass filter with a cutoff of 3.2MHz -# and a transition band width of .5MHz. Center of the tv channels is -# then at 0 with edges at -3.2MHz and 3.2MHz. - -from gnuradio import gr -from gnuradio import blocks -from gnuradio import filter -import sys, os - -def graph (args): - - print os.getpid() - - nargs = len (args) - if nargs == 1: - infile = args[0] - else: - sys.stderr.write('usage: interp.py input_file\n') - sys.exit (1) - - tb = gr.top_block() - - srcf = blocks.file_source(gr.sizeof_short,infile) - s2ss = blocks.stream_to_streams(gr.sizeof_short,2) - s2f1 = blocks.short_to_float() - s2f2 = blocks.short_to_float() - src0 = blocks.float_to_complex() - - - lp_coeffs = filter.firdes.low_pass(3, 19.2e6, 3.2e6, .5e6, - filter.firdes.WIN_HAMMING) - lp = filter.interp_fir_filter_ccf(3, lp_coeffs) - - file = blocks.file_sink(gr.sizeof_gr_complex,"/tmp/atsc_pipe_1") - - tb.connect( srcf, s2ss ) - tb.connect( (s2ss, 0), s2f1, (src0,0) ) - tb.connect( (s2ss, 1), s2f2, (src0,1) ) - tb.connect( src0, lp, file) - - tb.start() - raw_input ('Head End: Press Enter to stop') - tb.stop() - -if __name__ == '__main__': - graph (sys.argv[1:]) - - diff --git a/gr-atsc/python/atsc/viterbi-out.py b/gr-atsc/python/atsc/viterbi-out.py deleted file mode 100755 index cc60ffda42..0000000000 --- a/gr-atsc/python/atsc/viterbi-out.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -# Copyright 2004,2006,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# - -from gnuradio import gr, atsc -from gnuradio import blocks -import sys, os - -def main(args): - - print os.getpid() - - nargs = len (args) - if nargs == 1: - outfile = args[0] - else: - sys.stderr.write ('usage: viterbi_out.py output_file\n') - sys.exit (1) - - tb = gr.top_block() - - src = blocks.file_source(atsc.sizeof_atsc_soft_data_segment, "/tmp/atsc_pipe_5") - viterbi = atsc.viterbi_decoder() - deinter = atsc.deinterleaver() - rs_dec = atsc.rs_decoder() - derand = atsc.derandomizer() - depad = atsc.depad() - dst = blocks.file_sink(gr.sizeof_char,outfile) - tb.connect(src, viterbi, deinter, rs_dec, derand, depad, dst) - tb.run () - - -if __name__ == '__main__': - main(sys.argv[1:]) - - - - - - diff --git a/gr-atsc/python/atsc/xlate.py b/gr-atsc/python/atsc/xlate.py deleted file mode 100755 index eb04ecc68a..0000000000 --- a/gr-atsc/python/atsc/xlate.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env /usr/bin/python -# -# Copyright 2004,2007 Free Software Foundation, Inc. -# -# This file is part of GNU Radio -# -# GNU Radio is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3, or (at your option) -# any later version. -# -# GNU Radio is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with GNU Radio; see the file COPYING. If not, write to -# the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -# Boston, MA 02111-1307, USA. -# -# This module upconverts the 19.2e6 sample rate signal from a center -# of 0 to 5.75e6 and converts to float, to prepare the signal for -# the old gnuradio 0.9 block (bit timing loop, field sync checker, -# equalizer and field sync demux), effectively simulating an -# mc4020 card, except the sample rate is 19.2e6 instead of 20e6. -# -# The signal is then centered on 5.75e6 with edges at 5.75 + 3.2 = 8.95MHz -# and 5.75 - 3.2 = 2.55Mhz, low pass filtered with cutoff at 9Mhz and a -# transition band width of 1Mhz. -# -# Input complex -3.2 to 3.2Mhz, output float 2.55 to 8.95Mhz. - -from gnuradio import gr -from gnuradio import filter -from gnuradio import blocks -import os - -def graph (): - print os.getpid() - sampling_freq = 19200000 - - tb = gr.top_block () - - src0 = blocks.file_source(gr.sizeof_gr_complex,"/tmp/atsc_pipe_1") - - duc_coeffs = filter.firdes.low_pass( 1, 19.2e6, 9e6, 1e6, filter.firdes.WIN_HAMMING ) - duc = filter.freq_xlating_fir_filter_ccf( 1, duc_coeffs, 5.75e6, 19.2e6 ) - - c2f = blocks.complex_to_float() - file = blocks.file_sink(gr.sizeof_float,"/tmp/atsc_pipe_2") - - tb.connect( src0, duc, c2f, file ) - - tb.run() - -if __name__ == '__main__': - graph () diff --git a/gr-blocks/grc/blocks_block_tree.xml b/gr-blocks/grc/blocks_block_tree.xml index 150b3f1c93..c6c797f52a 100644 --- a/gr-blocks/grc/blocks_block_tree.xml +++ b/gr-blocks/grc/blocks_block_tree.xml @@ -53,6 +53,7 @@ <name>Control Port</name> <block>blocks_ctrlport_monitor</block> <block>blocks_ctrlport_monitor_performance</block> + <block>blocks_ctrlport_probe2_x</block> <block>blocks_ctrlport_probe2_c</block> <block>blocks_ctrlport_probe_c</block> </cat> @@ -62,6 +63,8 @@ <block>blocks_tag_debug</block> <block>blocks_message_debug</block> <block>blocks_random_pdu</block> + <block>blocks_message_strobe</block> + <block>blocks_tags_strobe</block> </cat> <cat> <name>File Operators</name> diff --git a/gr-blocks/grc/blocks_ctrlport_probe2_c.xml b/gr-blocks/grc/blocks_ctrlport_probe2_c.xml index 3add69aa7b..b5f0b089a7 100644 --- a/gr-blocks/grc/blocks_ctrlport_probe2_c.xml +++ b/gr-blocks/grc/blocks_ctrlport_probe2_c.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- - Copyright 2012 Free Software Foundation, Inc. + Copyright 2012-2013 Free Software Foundation, Inc. This file is part of GNU Radio @@ -22,23 +22,23 @@ --> <block> - <name>Ctrlport Complex Probe (fixed len)</name> + <name>Ctrlport Probe</name> <key>blocks_ctrlport_probe2_c</key> <import>from gnuradio import blocks</import> - <make>blocks.ctrlport_probe2_c($name, $desc, $len)</make> + <make>blocks.ctrlport_probe2_c($name, $desc, $len, $disp_mask)</make> <callback>set_length($len)</callback> <param> <name>Name</name> <key>name</key> - <value>constellation</value> + <value>samples</value> <type>string</type> </param> <param> <name>Description</name> <key>desc</key> - <value>Constellation Points</value> + <value>Sample Points</value> <type>string</type> </param> @@ -49,6 +49,32 @@ <type>int</type> </param> + <param> + <name>Display Mask</name> + <key>disp_mask</key> + <value>gr.DISPTIME</value> + <type>int</type> + <option> + <name>Constellation</name> + <key>gr.DISPXY | gr.DISPOPTSCATTER</key> + </option> + <option> + <name>Time</name> + <key>gr.DISPTIME</key> + </option> + <option> + <name>PSD</name> + <key>gr.DISPPSD</key> + </option> + <option> + <name>Spectrogram</name> + <key>gr.DISPSPEC</key> + </option> + <option> + <name>Raster</name> + <key>gr.DISPRAST</key> + </option> + </param> <sink> <name>in</name> @@ -56,9 +82,10 @@ </sink> <doc> - Place this in a graph to export complex values to a GRCP port probe. + Place this in a graph to export vectors of samples to a GRCP port probe. - * Version 2 allows you to specify a length in samples that you wish to get every probe + * Specify the number of samples to transmit at once and the type + of default display to use. </doc> </block> diff --git a/gr-blocks/grc/blocks_ctrlport_probe2_x.xml b/gr-blocks/grc/blocks_ctrlport_probe2_x.xml new file mode 100644 index 0000000000..b9b1660bc9 --- /dev/null +++ b/gr-blocks/grc/blocks_ctrlport_probe2_x.xml @@ -0,0 +1,123 @@ +<?xml version="1.0"?> + +<!-- + Copyright 2013 Free Software Foundation, Inc. + + This file is part of GNU Radio + + GNU Radio is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GNU Radio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNU Radio; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, + Boston, MA 02110-1301, USA. +--> + +<block> + <name>Ctrlport Probe</name> + <key>blocks_ctrlport_probe2_x</key> + <import>from gnuradio import blocks</import> + <make>blocks.ctrlport_probe2_$(type.fcn)($name, $desc, $len, $disp_mask)</make> + <callback>set_length($len)</callback> + + <param> + <name>Input Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>fcn:c</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>fcn:f</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>fcn:i</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>fcn:s</opt> + </option> + <option> + <name>Byte</name> + <key>byte</key> + <opt>fcn:b</opt> + </option> + </param> + + <param> + <name>Name</name> + <key>name</key> + <value>samples</value> + <type>string</type> + </param> + + <param> + <name>Description</name> + <key>desc</key> + <value>Sample Points</value> + <type>string</type> + </param> + + <param> + <name>Length</name> + <key>len</key> + <value>1024</value> + <type>int</type> + </param> + + <param> + <name>Display Mask</name> + <key>disp_mask</key> + <value>gr.DISPTIME</value> + <type>int</type> + <option> + <name>Constellation</name> + <key>gr.DISPXY | gr.DISPOPTSCATTER</key> + </option> + <option> + <name>Time</name> + <key>gr.DISPTIME</key> + </option> + <option> + <name>PSD</name> + <key>gr.DISPPSD</key> + </option> + <option> + <name>Spectrogram</name> + <key>gr.DISPSPEC</key> + </option> + <option> + <name>Raster</name> + <key>gr.DISPRAST</key> + </option> + </param> + + <sink> + <name>in</name> + <type>$type</type> + </sink> + + <doc> + Place this in a graph to export vectors of samples to a GRCP port probe. + + * Specify the number of samples to transmit at once and the type + of default display to use. + </doc> + +</block> + diff --git a/gr-blocks/grc/blocks_interleaved_char_to_complex.xml b/gr-blocks/grc/blocks_interleaved_char_to_complex.xml new file mode 100644 index 0000000000..9c0cdef6f8 --- /dev/null +++ b/gr-blocks/grc/blocks_interleaved_char_to_complex.xml @@ -0,0 +1,37 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Interleaved Char to Complex: +################################################### + --> +<block> + <name>IChar To Complex</name> + <key>blocks_interleaved_char_to_complex</key> + <import>from gnuradio import blocks</import> + <make>blocks.interleaved_char_to_complex($vector_input)</make> + <param> + <name>Vector Input</name> + <key>vector_input</key> + <value>False</value> + <type>enum</type> + <option> + <name>No</name> + <key>False</key> + <opt>vlen:1</opt> + </option> + <option> + <name>Yes</name> + <key>True</key> + <opt>vlen:2</opt> + </option> + </param> + <sink> + <name>in</name> + <type>byte</type> + <vlen>$vector_input.vlen</vlen> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-blocks/grc/blocks_tag_debug.xml b/gr-blocks/grc/blocks_tag_debug.xml index 929e53afed..bf2b4a7ec0 100644 --- a/gr-blocks/grc/blocks_tag_debug.xml +++ b/gr-blocks/grc/blocks_tag_debug.xml @@ -8,7 +8,7 @@ <name>Tag Debug</name> <key>blocks_tag_debug</key> <import>from gnuradio import blocks</import> - <make>blocks.tag_debug($type.size*$vlen, $name)</make> + <make>blocks.tag_debug($type.size*$vlen, $name); self.$(id).set_display($display)</make> <callback>set_display($display)</callback> <param> <name>Input Type</name> diff --git a/gr-blocks/grc/blocks_tags_strobe.xml b/gr-blocks/grc/blocks_tags_strobe.xml new file mode 100644 index 0000000000..16109eaad1 --- /dev/null +++ b/gr-blocks/grc/blocks_tags_strobe.xml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<!-- +################################################### +## Tags Strobe +################################################### + --> +<block> + <name>Tags Strobe</name> + <key>blocks_tags_strobe</key> + <import>from gnuradio import blocks</import> + <import>import pmt</import> + <make>blocks.tags_strobe($type.size*$vlen, $value, $nsamps)</make> + <callback>set_value($value)</callback> + <callback>set_nsamps($nsamps)</callback> + <param> + <name>Output Type</name> + <key>type</key> + <type>enum</type> + <option> + <name>Complex</name> + <key>complex</key> + <opt>size:gr.sizeof_gr_complex</opt> + </option> + <option> + <name>Float</name> + <key>float</key> + <opt>size:gr.sizeof_float</opt> + </option> + <option> + <name>Int</name> + <key>int</key> + <opt>size:gr.sizeof_int</opt> + </option> + <option> + <name>Short</name> + <key>short</key> + <opt>size:gr.sizeof_short</opt> + </option> + <option> + <name>Byte</name> + <key>byte</key> + <opt>size:gr.sizeof_char</opt> + </option> + </param> + <param> + <name>Value (PMT)</name> + <key>value</key> + <value>pmt.intern("TEST")</value> + <type>raw</type> + </param> + <param> + <name>Num. Samples</name> + <key>nsamps</key> + <value>1000</value> + <type>int</type> + </param> + <param> + <name>Vec Length</name> + <key>vlen</key> + <value>1</value> + <type>int</type> + </param> + <check>$vlen > 0</check> + <source> + <name>out</name> + <type>$type</type> + <vlen>$vlen</vlen> + </source> +</block> diff --git a/gr-blocks/grc/blocks_vco_c.xml b/gr-blocks/grc/blocks_vco_c.xml new file mode 100644 index 0000000000..f6246441b9 --- /dev/null +++ b/gr-blocks/grc/blocks_vco_c.xml @@ -0,0 +1,35 @@ +<?xml version="1.0"?> +<!-- +################################################### +##VCO +################################################### + --> +<block> + <name>VCO (complex)</name> + <key>blocks_vco_c</key> + <import>from gnuradio import blocks</import> + <make>blocks.vco_c($samp_rate, $sensitivity, $amplitude)</make> + <param> + <name>Sample Rate</name> + <key>samp_rate</key> + <type>real</type> + </param> + <param> + <name>Sensitivity</name> + <key>sensitivity</key> + <type>real</type> + </param> + <param> + <name>Amplitude</name> + <key>amplitude</key> + <type>real</type> + </param> + <sink> + <name>in</name> + <type>float</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt index 13e1ac71b8..de52e3e3e7 100644 --- a/gr-blocks/include/gnuradio/blocks/CMakeLists.txt +++ b/gr-blocks/include/gnuradio/blocks/CMakeLists.txt @@ -146,6 +146,7 @@ install(FILES int_to_float.h interleave.h interleaved_short_to_complex.h + interleaved_char_to_complex.h keep_m_in_n.h keep_one_in_n.h lfsr_32k_source_s.h @@ -191,6 +192,7 @@ install(FILES tagged_file_sink.h tagged_stream_mux.h tagged_stream_to_pdu.h + tags_strobe.h threshold_ff.h throttle.h transcendental.h @@ -200,6 +202,7 @@ install(FILES udp_source.h unpack_k_bits_bb.h vco_f.h + vco_c.h vector_map.h vector_to_stream.h vector_to_streams.h @@ -213,6 +216,10 @@ if(ENABLE_GR_CTRLPORT) install(FILES ctrlport_probe_c.h ctrlport_probe2_c.h + ctrlport_probe2_f.h + ctrlport_probe2_s.h + ctrlport_probe2_i.h + ctrlport_probe2_b.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/blocks COMPONENT "blocks_devel" ) diff --git a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_b.h b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_b.h new file mode 100644 index 0000000000..5ad31655cf --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_b.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_B_H +#define INCLUDED_CTRLPORT_PROBE2_B_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief A ControlPort probe to export vectors of signals. + * \ingroup measurement_tools_blk + * \ingroup controlport_blk + * + * \details + * This block acts as a sink in the flowgraph but also exports + * vectors of complex samples over ControlPort. This block holds + * the latest \p len number of complex samples so that every query + * by a ControlPort client will get the same length vector. + */ + class BLOCKS_API ctrlport_probe2_b : virtual public sync_block + { + public: + // gr::blocks::ctrlport_probe2_b::sptr + typedef boost::shared_ptr<ctrlport_probe2_b> sptr; + + /*! + * \brief Make a ControlPort probe block. + * \param id A string ID to name the probe over ControlPort. + * \param desc A string describing the probe. + * \param len Number of samples to transmit. + * \param disp_mask Mask to set default display params. + */ + static sptr make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + + virtual std::vector<signed char> get() = 0; + + virtual void set_length(int len) = 0; + virtual int length() const = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_B_H */ + diff --git a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_c.h b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_c.h index 3841c43b74..51d908b47a 100644 --- a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_c.h +++ b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_c.h @@ -51,8 +51,10 @@ namespace gr { * \param id A string ID to name the probe over ControlPort. * \param desc A string describing the probe. * \param len Number of samples to transmit. + * \param disp_mask Mask to set default display params. */ - static sptr make(const std::string &id, const std::string &desc, int len); + static sptr make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); virtual std::vector<gr_complex> get() = 0; diff --git a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_f.h b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_f.h new file mode 100644 index 0000000000..38d1906ed0 --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_f.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_F_H +#define INCLUDED_CTRLPORT_PROBE2_F_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief A ControlPort probe to export vectors of signals. + * \ingroup measurement_tools_blk + * \ingroup controlport_blk + * + * \details + * This block acts as a sink in the flowgraph but also exports + * vectors of complex samples over ControlPort. This block holds + * the latest \p len number of complex samples so that every query + * by a ControlPort client will get the same length vector. + */ + class BLOCKS_API ctrlport_probe2_f : virtual public sync_block + { + public: + // gr::blocks::ctrlport_probe2_f::sptr + typedef boost::shared_ptr<ctrlport_probe2_f> sptr; + + /*! + * \brief Make a ControlPort probe block. + * \param id A string ID to name the probe over ControlPort. + * \param desc A string describing the probe. + * \param len Number of samples to transmit. + * \param disp_mask Mask to set default display params. + */ + static sptr make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + + virtual std::vector<float> get() = 0; + + virtual void set_length(int len) = 0; + virtual int length() const = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_F_H */ + diff --git a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_i.h b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_i.h new file mode 100644 index 0000000000..cdfb1b86a4 --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_i.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_I_H +#define INCLUDED_CTRLPORT_PROBE2_I_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief A ControlPort probe to export vectors of signals. + * \ingroup measurement_tools_blk + * \ingroup controlport_blk + * + * \details + * This block acts as a sink in the flowgraph but also exports + * vectors of complex samples over ControlPort. This block holds + * the latest \p len number of complex samples so that every query + * by a ControlPort client will get the same length vector. + */ + class BLOCKS_API ctrlport_probe2_i : virtual public sync_block + { + public: + // gr::blocks::ctrlport_probe2_i::sptr + typedef boost::shared_ptr<ctrlport_probe2_i> sptr; + + /*! + * \brief Make a ControlPort probe block. + * \param id A string ID to name the probe over ControlPort. + * \param desc A string describing the probe. + * \param len Number of samples to transmit. + * \param disp_mask Mask to set default display params. + */ + static sptr make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + + virtual std::vector<int> get() = 0; + + virtual void set_length(int len) = 0; + virtual int length() const = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_I_H */ + diff --git a/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_s.h b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_s.h new file mode 100644 index 0000000000..676a24822a --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/ctrlport_probe2_s.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_S_H +#define INCLUDED_CTRLPORT_PROBE2_S_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief A ControlPort probe to export vectors of signals. + * \ingroup measurement_tools_blk + * \ingroup controlport_blk + * + * \details + * This block acts as a sink in the flowgraph but also exports + * vectors of complex samples over ControlPort. This block holds + * the latest \p len number of complex samples so that every query + * by a ControlPort client will get the same length vector. + */ + class BLOCKS_API ctrlport_probe2_s : virtual public sync_block + { + public: + // gr::blocks::ctrlport_probe2_s::sptr + typedef boost::shared_ptr<ctrlport_probe2_s> sptr; + + /*! + * \brief Make a ControlPort probe block. + * \param id A string ID to name the probe over ControlPort. + * \param desc A string describing the probe. + * \param len Number of samples to transmit. + * \param disp_mask Mask to set default display params. + */ + static sptr make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + + virtual std::vector<short> get() = 0; + + virtual void set_length(int len) = 0; + virtual int length() const = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_S_H */ + diff --git a/gr-blocks/include/gnuradio/blocks/interleaved_char_to_complex.h b/gr-blocks/include/gnuradio/blocks/interleaved_char_to_complex.h new file mode 100644 index 0000000000..53a77e7cd0 --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/interleaved_char_to_complex.h @@ -0,0 +1,51 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_BLOCKS_INTERLEAVED_CHAR_TO_COMPLEX_H +#define INCLUDED_BLOCKS_INTERLEAVED_CHAR_TO_COMPLEX_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_decimator.h> + +namespace gr { + namespace blocks { + + /*! + * \brief Convert stream of interleaved chars to a stream of complex + * \ingroup type_converters_blk + */ + class BLOCKS_API interleaved_char_to_complex : virtual public sync_decimator + { + public: + // gr::blocks::interleaved_char_to_complex::sptr + typedef boost::shared_ptr<interleaved_char_to_complex> sptr; + + /*! + * Build an interleaved char to complex block. + */ + static sptr make(bool vector_input=false); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_BLOCKS_INTERLEAVED_CHAR_TO_COMPLEX_H */ diff --git a/gr-blocks/include/gnuradio/blocks/tag_debug.h b/gr-blocks/include/gnuradio/blocks/tag_debug.h index 8368713175..ab222f02f1 100644 --- a/gr-blocks/include/gnuradio/blocks/tag_debug.h +++ b/gr-blocks/include/gnuradio/blocks/tag_debug.h @@ -72,6 +72,11 @@ namespace gr { virtual std::vector<tag_t> current_tags() = 0; /*! + * \brief Return the total number of tags in the tag queue. + */ + virtual int num_tags() = 0; + + /*! * \brief Set the display of tags to stdout on/off. */ virtual void set_display(bool d) = 0; diff --git a/gr-blocks/include/gnuradio/blocks/tags_strobe.h b/gr-blocks/include/gnuradio/blocks/tags_strobe.h new file mode 100644 index 0000000000..3660ca4c1b --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/tags_strobe.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_TAGS_STROBE_H +#define INCLUDED_GR_TAGS_STROBE_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief Send tags at defined interval + * \ingroup debug_blk + * + * \details + * Sends a tag with key 'strobe' and a user-defined value (as a + * PMT) every \p nsamps number of samples. Useful for + * testing/debugging the tags system. + * + * Because tags are sent with a data stream, this is a source + * block that acts identical to a null_source block. + */ + class BLOCKS_API tags_strobe : virtual public sync_block + { + public: + // gr::blocks::tags_strobe::sptr + typedef boost::shared_ptr<tags_strobe> sptr; + + /*! + * Make a tags stobe block to send tags with value \p value + * every \p nsamps number of samples. + * + * \param sizeof_stream_item size of the stream items in bytes. + * \param value The value of the tags to send, as a PMT. + * \param nsamps the number of samples between each tag. + */ + static sptr make(size_t sizeof_stream_item, + pmt::pmt_t value, uint64_t nsamps); + + /*! + * Reset the value of the tags being sent. + * \param value The value of the tags to send as a PMT. + */ + virtual void set_value(pmt::pmt_t value) = 0; + + /*! + * Get the value of the tags being sent. + */ + virtual pmt::pmt_t value() const = 0; + + /*! + * Reset the sending interval. + * \param nsamps the number of samples between each tag + */ + virtual void set_nsamps(uint64_t nsamps) = 0; + + /*! + * Get the number of samples between the tag strobe. + */ + virtual uint64_t nsamps() const = 0; + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_TAGS_STROBE_H */ diff --git a/gr-blocks/include/gnuradio/blocks/vco_c.h b/gr-blocks/include/gnuradio/blocks/vco_c.h new file mode 100644 index 0000000000..ab9723af5b --- /dev/null +++ b/gr-blocks/include/gnuradio/blocks/vco_c.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_VCO_C_H +#define INCLUDED_GR_VCO_C_H + +#include <gnuradio/blocks/api.h> +#include <gnuradio/sync_block.h> + +namespace gr { + namespace blocks { + + /*! + * \brief VCO - Voltage controlled oscillator + * \ingroup modulators_blk + * \ingroup waveform_generators_blk + * + * \details + * input: float stream of control voltages; output: complex oscillator output + */ + class BLOCKS_API vco_c : virtual public sync_block + { + public: + // gr::blocks::vco_c::sptr + typedef boost::shared_ptr<vco_c> sptr; + + /*! + * \brief VCO - Voltage controlled oscillator + * + * \param sampling_rate sampling rate (Hz) + * \param sensitivity units are radians/sec/volt + * \param amplitude output amplitude + */ + static sptr make(double sampling_rate, double sensitivity, double amplitude); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_VCO_C_H */ diff --git a/gr-blocks/lib/CMakeLists.txt b/gr-blocks/lib/CMakeLists.txt index 529c6de054..1c7bd80ae0 100644 --- a/gr-blocks/lib/CMakeLists.txt +++ b/gr-blocks/lib/CMakeLists.txt @@ -186,6 +186,7 @@ list(APPEND gr_blocks_sources interleave_impl.cc interleaved_short_array_to_complex.cc interleaved_short_to_complex_impl.cc + interleaved_char_to_complex_impl.cc keep_m_in_n_impl.cc keep_one_in_n_impl.cc lfsr_32k_source_s_impl.cc @@ -230,6 +231,7 @@ list(APPEND gr_blocks_sources stretch_ff_impl.cc tagged_file_sink_impl.cc tagged_stream_to_pdu_impl.cc + tags_strobe_impl.cc threshold_ff_impl.cc throttle_impl.cc transcendental_impl.cc @@ -242,6 +244,7 @@ list(APPEND gr_blocks_sources udp_source_impl.cc unpack_k_bits_bb_impl.cc vco_f_impl.cc + vco_c_impl.cc vector_map_impl.cc vector_to_stream_impl.cc vector_to_streams_impl.cc @@ -253,6 +256,10 @@ if(ENABLE_GR_CTRLPORT) list(APPEND gr_blocks_sources ctrlport_probe_c_impl.cc ctrlport_probe2_c_impl.cc + ctrlport_probe2_f_impl.cc + ctrlport_probe2_s_impl.cc + ctrlport_probe2_i_impl.cc + ctrlport_probe2_b_impl.cc ) endif(ENABLE_GR_CTRLPORT) diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.cc b/gr-blocks/lib/ctrlport_probe2_b_impl.cc new file mode 100644 index 0000000000..74c2980a7a --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.cc @@ -0,0 +1,162 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ctrlport_probe2_b_impl.h" +#include <gnuradio/io_signature.h> + +namespace gr { + namespace blocks { + + ctrlport_probe2_b::sptr + ctrlport_probe2_b::make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask) + { + return gnuradio::get_initial_sptr + (new ctrlport_probe2_b_impl(id, desc, len, disp_mask)); + } + + ctrlport_probe2_b_impl::ctrlport_probe2_b_impl(const std::string &id, + const std::string &desc, + int len, unsigned int disp_mask) + : sync_block("probe2_b", + io_signature::make(1, 1, sizeof(char)), + io_signature::make(0, 0, 0)), + d_id(id), d_desc(desc), d_len(len), d_disp_mask(disp_mask) + { + set_length(len); + } + + ctrlport_probe2_b_impl::~ctrlport_probe2_b_impl() + { + } + + void + ctrlport_probe2_b_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + // make sure all inputs have noutput_items available + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = d_len; + } + + // boost::shared_mutex mutex_buffer; + // mutable boost::mutex mutex_notify; + // boost::condition_variable condition_buffer_ready; + std::vector<signed char> + ctrlport_probe2_b_impl::get() + { + mutex_buffer.lock(); + d_buffer.clear(); + mutex_buffer.unlock(); + + // wait for condition + boost::mutex::scoped_lock lock(mutex_notify); + condition_buffer_ready.wait(lock); + + mutex_buffer.lock(); + std::vector<signed char> buf_copy = d_buffer; + assert(buf_copy.size() == d_len); + mutex_buffer.unlock(); + + return buf_copy; + } + + void + ctrlport_probe2_b_impl::set_length(int len) + { + if(len > 8191) { + std::cerr << "probe2_b: length " << len + << " exceeds maximum buffer size of 8191" << std::endl; + len = 8191; + } + + d_len = len; + d_buffer.reserve(d_len); + } + + int + ctrlport_probe2_b_impl::length() const + { + return (int)d_len; + } + + int + ctrlport_probe2_b_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const char *in = (const char*)input_items[0]; + + // copy samples to get buffer if we need samples + mutex_buffer.lock(); + if(d_buffer.size() < d_len) { + // copy smaller of remaining buffer space and num inputs to work() + int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + + // TODO: convert this to a copy operator for speed... + for(int i = 0; i < num_copy; i++) { + d_buffer.push_back(in[i]); + } + + // notify the waiting get() if we fill up the buffer + if(d_buffer.size() == d_len) { + condition_buffer_ready.notify_one(); + } + } + mutex_buffer.unlock(); + + return noutput_items; + } + + void + ctrlport_probe2_b_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + int len = static_cast<int>(d_len); + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_b, std::vector<signed char> >( + alias(), d_id.c_str(), &ctrlport_probe2_b::get, + pmt::mp(-128), pmt::mp(127), pmt::mp(0), + "volts", d_desc.c_str(), RPC_PRIVLVL_MIN, + d_disp_mask))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_b, int>( + alias(), "length", &ctrlport_probe2_b::length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "get vector length", RPC_PRIVLVL_MIN, DISPNULL))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_set<ctrlport_probe2_b, int>( + alias(), "length", &ctrlport_probe2_b::set_length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "set vector length", RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/ctrlport_probe2_b_impl.h b/gr-blocks/lib/ctrlport_probe2_b_impl.h new file mode 100644 index 0000000000..155dd4c23c --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_b_impl.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_B_IMPL_H +#define INCLUDED_CTRLPORT_PROBE2_B_IMPL_H + +#include <gnuradio/blocks/ctrlport_probe2_b.h> +#include <gnuradio/rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +namespace gr { + namespace blocks { + + class ctrlport_probe2_b_impl : public ctrlport_probe2_b + { + private: + std::string d_id; + std::string d_desc; + size_t d_len; + unsigned int d_disp_mask; + boost::shared_mutex mutex_buffer; + mutable boost::mutex mutex_notify; + boost::condition_variable condition_buffer_ready; + + std::vector<signed char> d_buffer; + + public: + ctrlport_probe2_b_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + ~ctrlport_probe2_b_impl(); + + void setup_rpc(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + std::vector<signed char> get(); + + void set_length(int len); + int length() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_C_IMPL_H */ + diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.cc b/gr-blocks/lib/ctrlport_probe2_c_impl.cc index bc6febdb2d..3e324b0d00 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.cc +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.cc @@ -32,18 +32,20 @@ namespace gr { ctrlport_probe2_c::sptr ctrlport_probe2_c::make(const std::string &id, - const std::string &desc, int len) + const std::string &desc, + int len, unsigned int disp_mask) { return gnuradio::get_initial_sptr - (new ctrlport_probe2_c_impl(id, desc, len)); + (new ctrlport_probe2_c_impl(id, desc, len, disp_mask)); } ctrlport_probe2_c_impl::ctrlport_probe2_c_impl(const std::string &id, - const std::string &desc, int len) + const std::string &desc, + int len, unsigned int disp_mask) : sync_block("probe2_c", io_signature::make(1, 1, sizeof(gr_complex)), io_signature::make(0, 0, 0)), - d_id(id), d_desc(desc), d_len(len) + d_id(id), d_desc(desc), d_len(len), d_disp_mask(disp_mask) { set_length(len); } @@ -139,11 +141,9 @@ namespace gr { d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_c, std::vector<std::complex<float> > >( alias(), d_id.c_str(), &ctrlport_probe2_c::get, - pmt::make_c32vector(0,-2), - pmt::make_c32vector(0,2), - pmt::make_c32vector(0,0), + pmt::mp(-2), pmt::mp(2), pmt::mp(0), "volts", d_desc.c_str(), RPC_PRIVLVL_MIN, - DISPXY | DISPOPTSCATTER))); + d_disp_mask | DISPOPTCPLX))); d_rpc_vars.push_back( rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_c, int>( diff --git a/gr-blocks/lib/ctrlport_probe2_c_impl.h b/gr-blocks/lib/ctrlport_probe2_c_impl.h index c58d97f078..15ff0f4ea2 100644 --- a/gr-blocks/lib/ctrlport_probe2_c_impl.h +++ b/gr-blocks/lib/ctrlport_probe2_c_impl.h @@ -36,6 +36,7 @@ namespace gr { std::string d_id; std::string d_desc; size_t d_len; + unsigned int d_disp_mask; boost::shared_mutex mutex_buffer; mutable boost::mutex mutex_notify; boost::condition_variable condition_buffer_ready; @@ -43,7 +44,8 @@ namespace gr { std::vector<gr_complex> d_buffer; public: - ctrlport_probe2_c_impl(const std::string &id, const std::string &desc, int len); + ctrlport_probe2_c_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); ~ctrlport_probe2_c_impl(); void setup_rpc(); diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.cc b/gr-blocks/lib/ctrlport_probe2_f_impl.cc new file mode 100644 index 0000000000..3c723f8834 --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.cc @@ -0,0 +1,164 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ctrlport_probe2_f_impl.h" +#include <gnuradio/io_signature.h> + +namespace gr { + namespace blocks { + + ctrlport_probe2_f::sptr + ctrlport_probe2_f::make(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask) + { + return gnuradio::get_initial_sptr + (new ctrlport_probe2_f_impl(id, desc, len, disp_mask)); + } + + ctrlport_probe2_f_impl::ctrlport_probe2_f_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask) + : sync_block("probe2_f", + io_signature::make(1, 1, sizeof(float)), + io_signature::make(0, 0, 0)), + d_id(id), d_desc(desc), d_len(len), d_disp_mask(disp_mask) + { + set_length(len); + } + + ctrlport_probe2_f_impl::~ctrlport_probe2_f_impl() + { + } + + void + ctrlport_probe2_f_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + // make sure all inputs have noutput_items available + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = d_len; + } + + // boost::shared_mutex mutex_buffer; + // mutable boost::mutex mutex_notify; + // boost::condition_variable condition_buffer_ready; + std::vector<float> + ctrlport_probe2_f_impl::get() + { + mutex_buffer.lock(); + d_buffer.clear(); + mutex_buffer.unlock(); + + // wait for condition + boost::mutex::scoped_lock lock(mutex_notify); + condition_buffer_ready.wait(lock); + + mutex_buffer.lock(); + std::vector<float> buf_copy = d_buffer; + assert(buf_copy.size() == d_len); + mutex_buffer.unlock(); + + return buf_copy; + } + + void + ctrlport_probe2_f_impl::set_length(int len) + { + if(len > 8191) { + std::cerr << "probe2_f: length " << len + << " exceeds maximum buffer size of 8191" << std::endl; + len = 8191; + } + + d_len = len; + d_buffer.reserve(d_len); + } + + int + ctrlport_probe2_f_impl::length() const + { + return (int)d_len; + } + + int + ctrlport_probe2_f_impl::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]; + + // copy samples to get buffer if we need samples + mutex_buffer.lock(); + if(d_buffer.size() < d_len) { + // copy smaller of remaining buffer space and num inputs to work() + int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + + // TODO: convert this to a copy operator for speed... + for(int i = 0; i < num_copy; i++) { + d_buffer.push_back(in[i]); + } + + // notify the waiting get() if we fill up the buffer + if(d_buffer.size() == d_len) { + condition_buffer_ready.notify_one(); + } + } + mutex_buffer.unlock(); + + return noutput_items; + } + + void + ctrlport_probe2_f_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + int len = static_cast<int>(d_len); + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_f, std::vector<float> >( + alias(), d_id.c_str(), &ctrlport_probe2_f::get, + pmt::mp(-2.0f), pmt::mp(2.0f), pmt::mp(0.0f), +// pmt::make_f32vector(1,-2), +// pmt::make_f32vector(1,2), +// pmt::make_f32vector(1,0), + "volts", d_desc.c_str(), RPC_PRIVLVL_MIN, + d_disp_mask))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_f, int>( + alias(), "length", &ctrlport_probe2_f::length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "get vector length", RPC_PRIVLVL_MIN, DISPNULL))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_set<ctrlport_probe2_f, int>( + alias(), "length", &ctrlport_probe2_f::set_length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "set vector length", RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/ctrlport_probe2_f_impl.h b/gr-blocks/lib/ctrlport_probe2_f_impl.h new file mode 100644 index 0000000000..a4aa099237 --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_f_impl.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_F_IMPL_H +#define INCLUDED_CTRLPORT_PROBE2_F_IMPL_H + +#include <gnuradio/blocks/ctrlport_probe2_f.h> +#include <gnuradio/rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +namespace gr { + namespace blocks { + + class ctrlport_probe2_f_impl : public ctrlport_probe2_f + { + private: + std::string d_id; + std::string d_desc; + size_t d_len; + unsigned int d_disp_mask; + boost::shared_mutex mutex_buffer; + mutable boost::mutex mutex_notify; + boost::condition_variable condition_buffer_ready; + + std::vector<float> d_buffer; + + public: + ctrlport_probe2_f_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + ~ctrlport_probe2_f_impl(); + + void setup_rpc(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + std::vector<float> get(); + + void set_length(int len); + int length() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_F_IMPL_H */ + diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.cc b/gr-blocks/lib/ctrlport_probe2_i_impl.cc new file mode 100644 index 0000000000..be7ae4d9d3 --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.cc @@ -0,0 +1,163 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ctrlport_probe2_i_impl.h" +#include <gnuradio/io_signature.h> + +namespace gr { + namespace blocks { + + ctrlport_probe2_i::sptr + ctrlport_probe2_i::make(const std::string &id, + const std::string &desc, + int len, unsigned int disp_mask) + { + return gnuradio::get_initial_sptr + (new ctrlport_probe2_i_impl(id, desc, len, disp_mask)); + } + + ctrlport_probe2_i_impl::ctrlport_probe2_i_impl(const std::string &id, + const std::string &desc, + int len, unsigned int disp_mask) + : sync_block("probe2_i", + io_signature::make(1, 1, sizeof(int)), + io_signature::make(0, 0, 0)), + d_id(id), d_desc(desc), d_len(len), d_disp_mask(disp_mask) + { + set_length(len); + } + + ctrlport_probe2_i_impl::~ctrlport_probe2_i_impl() + { + } + + void + ctrlport_probe2_i_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + // make sure all inputs have noutput_items available + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = d_len; + } + + // boost::shared_mutex mutex_buffer; + // mutable boost::mutex mutex_notify; + // boost::condition_variable condition_buffer_ready; + std::vector<int> + ctrlport_probe2_i_impl::get() + { + mutex_buffer.lock(); + d_buffer.clear(); + mutex_buffer.unlock(); + + // wait for condition + boost::mutex::scoped_lock lock(mutex_notify); + condition_buffer_ready.wait(lock); + + mutex_buffer.lock(); + std::vector<int> buf_copy = d_buffer; + assert(buf_copy.size() == d_len); + mutex_buffer.unlock(); + + return buf_copy; + } + + void + ctrlport_probe2_i_impl::set_length(int len) + { + if(len > 8191) { + std::cerr << "probe2_i: length " << len + << " exceeds maximum buffer size of 8191" << std::endl; + len = 8191; + } + + d_len = len; + d_buffer.reserve(d_len); + } + + int + ctrlport_probe2_i_impl::length() const + { + return (int)d_len; + } + + int + ctrlport_probe2_i_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const int *in = (const int*)input_items[0]; + + // copy samples to get buffer if we need samples + mutex_buffer.lock(); + if(d_buffer.size() < d_len) { + // copy smaller of remaining buffer space and num inputs to work() + int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + + // TODO: convert this to a copy operator for speed... + for(int i = 0; i < num_copy; i++) { + d_buffer.push_back(in[i]); + } + + // notify the waiting get() if we fill up the buffer + if(d_buffer.size() == d_len) { + condition_buffer_ready.notify_one(); + } + } + mutex_buffer.unlock(); + + return noutput_items; + } + + void + ctrlport_probe2_i_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + int len = static_cast<int>(d_len); + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_i, std::vector<int> >( + alias(), d_id.c_str(), &ctrlport_probe2_i::get, + pmt::mp(-32768), pmt::mp(32767), pmt::mp(0), + "volts", d_desc.c_str(), RPC_PRIVLVL_MIN, + d_disp_mask))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_i, int>( + alias(), "length", &ctrlport_probe2_i::length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "get vector length", RPC_PRIVLVL_MIN, DISPNULL))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_set<ctrlport_probe2_i, int>( + alias(), "length", &ctrlport_probe2_i::set_length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "set vector length", RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/ctrlport_probe2_i_impl.h b/gr-blocks/lib/ctrlport_probe2_i_impl.h new file mode 100644 index 0000000000..06493ac23a --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_i_impl.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_I_IMPL_H +#define INCLUDED_CTRLPORT_PROBE2_I_IMPL_H + +#include <gnuradio/blocks/ctrlport_probe2_i.h> +#include <gnuradio/rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +namespace gr { + namespace blocks { + + class ctrlport_probe2_i_impl : public ctrlport_probe2_i + { + private: + std::string d_id; + std::string d_desc; + size_t d_len; + unsigned int d_disp_mask; + boost::shared_mutex mutex_buffer; + mutable boost::mutex mutex_notify; + boost::condition_variable condition_buffer_ready; + + std::vector<int> d_buffer; + + public: + ctrlport_probe2_i_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + ~ctrlport_probe2_i_impl(); + + void setup_rpc(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + std::vector<int> get(); + + void set_length(int len); + int length() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_I_IMPL_H */ + diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.cc b/gr-blocks/lib/ctrlport_probe2_s_impl.cc new file mode 100644 index 0000000000..b10c419eb5 --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.cc @@ -0,0 +1,163 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "ctrlport_probe2_s_impl.h" +#include <gnuradio/io_signature.h> + +namespace gr { + namespace blocks { + + ctrlport_probe2_s::sptr + ctrlport_probe2_s::make(const std::string &id, + const std::string &desc, + int len, unsigned disp_mask) + { + return gnuradio::get_initial_sptr + (new ctrlport_probe2_s_impl(id, desc, len, disp_mask)); + } + + ctrlport_probe2_s_impl::ctrlport_probe2_s_impl(const std::string &id, + const std::string &desc, + int len, unsigned disp_mask) + : sync_block("probe2_s", + io_signature::make(1, 1, sizeof(short)), + io_signature::make(0, 0, 0)), + d_id(id), d_desc(desc), d_len(len), d_disp_mask(disp_mask) + { + set_length(len); + } + + ctrlport_probe2_s_impl::~ctrlport_probe2_s_impl() + { + } + + void + ctrlport_probe2_s_impl::forecast(int noutput_items, + gr_vector_int &ninput_items_required) + { + // make sure all inputs have noutput_items available + unsigned ninputs = ninput_items_required.size(); + for(unsigned i = 0; i < ninputs; i++) + ninput_items_required[i] = d_len; + } + + // boost::shared_mutex mutex_buffer; + // mutable boost::mutex mutex_notify; + // boost::condition_variable condition_buffer_ready; + std::vector<short> + ctrlport_probe2_s_impl::get() + { + mutex_buffer.lock(); + d_buffer.clear(); + mutex_buffer.unlock(); + + // wait for condition + boost::mutex::scoped_lock lock(mutex_notify); + condition_buffer_ready.wait(lock); + + mutex_buffer.lock(); + std::vector<short> buf_copy = d_buffer; + assert(buf_copy.size() == d_len); + mutex_buffer.unlock(); + + return buf_copy; + } + + void + ctrlport_probe2_s_impl::set_length(int len) + { + if(len > 8191) { + std::cerr << "probe2_s: length " << len + << " exceeds maximum buffer size of 8191" << std::endl; + len = 8191; + } + + d_len = len; + d_buffer.reserve(d_len); + } + + int + ctrlport_probe2_s_impl::length() const + { + return (int)d_len; + } + + int + ctrlport_probe2_s_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const short *in = (const short*)input_items[0]; + + // copy samples to get buffer if we need samples + mutex_buffer.lock(); + if(d_buffer.size() < d_len) { + // copy smaller of remaining buffer space and num inputs to work() + int num_copy = std::min( (int)(d_len - d_buffer.size()), noutput_items ); + + // TODO: convert this to a copy operator for speed... + for(int i = 0; i < num_copy; i++) { + d_buffer.push_back(in[i]); + } + + // notify the waiting get() if we fill up the buffer + if(d_buffer.size() == d_len) { + condition_buffer_ready.notify_one(); + } + } + mutex_buffer.unlock(); + + return noutput_items; + } + + void + ctrlport_probe2_s_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + int len = static_cast<int>(d_len); + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_s, std::vector<short> >( + alias(), d_id.c_str(), &ctrlport_probe2_s::get, + pmt::mp(-32768), pmt::mp(32767), pmt::mp(0), + "volts", d_desc.c_str(), RPC_PRIVLVL_MIN, + d_disp_mask))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_get<ctrlport_probe2_s, int>( + alias(), "length", &ctrlport_probe2_s::length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "get vector length", RPC_PRIVLVL_MIN, DISPNULL))); + + d_rpc_vars.push_back( + rpcbasic_sptr(new rpcbasic_register_set<ctrlport_probe2_s, int>( + alias(), "length", &ctrlport_probe2_s::set_length, + pmt::mp(1), pmt::mp(10*len), pmt::mp(len), + "samples", "set vector length", RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/ctrlport_probe2_s_impl.h b/gr-blocks/lib/ctrlport_probe2_s_impl.h new file mode 100644 index 0000000000..078dd56b73 --- /dev/null +++ b/gr-blocks/lib/ctrlport_probe2_s_impl.h @@ -0,0 +1,69 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CTRLPORT_PROBE2_S_IMPL_H +#define INCLUDED_CTRLPORT_PROBE2_S_IMPL_H + +#include <gnuradio/blocks/ctrlport_probe2_s.h> +#include <gnuradio/rpcregisterhelpers.h> +#include <boost/thread/shared_mutex.hpp> + +namespace gr { + namespace blocks { + + class ctrlport_probe2_s_impl : public ctrlport_probe2_s + { + private: + std::string d_id; + std::string d_desc; + size_t d_len; + unsigned int d_disp_mask; + boost::shared_mutex mutex_buffer; + mutable boost::mutex mutex_notify; + boost::condition_variable condition_buffer_ready; + + std::vector<short> d_buffer; + + public: + ctrlport_probe2_s_impl(const std::string &id, const std::string &desc, + int len, unsigned int disp_mask); + ~ctrlport_probe2_s_impl(); + + void setup_rpc(); + + void forecast(int noutput_items, gr_vector_int &ninput_items_required); + + std::vector<short> get(); + + void set_length(int len); + int length() const; + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_CTRLPORT_PROBE2_S_IMPL_H */ + diff --git a/gr-blocks/lib/interleaved_char_to_complex_impl.cc b/gr-blocks/lib/interleaved_char_to_complex_impl.cc new file mode 100644 index 0000000000..34114e3651 --- /dev/null +++ b/gr-blocks/lib/interleaved_char_to_complex_impl.cc @@ -0,0 +1,65 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "interleaved_char_to_complex_impl.h" +#include <gnuradio/io_signature.h> +#include <volk/volk.h> + +namespace gr { + namespace blocks { + + interleaved_char_to_complex::sptr interleaved_char_to_complex::make(bool vector_input) + { + return gnuradio::get_initial_sptr(new interleaved_char_to_complex_impl(vector_input)); + } + + interleaved_char_to_complex_impl::interleaved_char_to_complex_impl(bool vector_input) + : sync_decimator("interleaved_char_to_complex", + gr::io_signature::make (1, 1, (vector_input?2:1)*sizeof(char)), + gr::io_signature::make (1, 1, sizeof(gr_complex)), + vector_input?1:2), + d_vector_input(vector_input) + { + const int alignment_multiple = + volk_get_alignment() / sizeof(gr_complex); + set_alignment(std::max(1, alignment_multiple)); + } + + int + interleaved_char_to_complex_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const int8_t *in = (const int8_t *) input_items[0]; + float *out = (float *) output_items[0]; + + volk_8i_s32f_convert_32f_u(out, in, 1.0, 2*noutput_items); + + return noutput_items; + } + + } /* namespace blocks */ +}/* namespace gr */ diff --git a/gr-blocks/lib/interleaved_char_to_complex_impl.h b/gr-blocks/lib/interleaved_char_to_complex_impl.h new file mode 100644 index 0000000000..e815f08a1e --- /dev/null +++ b/gr-blocks/lib/interleaved_char_to_complex_impl.h @@ -0,0 +1,47 @@ +/* -*- c++ -*- */ +/* + * Copyright 2012 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_INTERLEAVED_CHAR_TO_COMPLEX_IMPL_H +#define INCLUDED_INTERLEAVED_CHAR_TO_COMPLEX_IMPL_H + +#include <gnuradio/blocks/interleaved_char_to_complex.h> + +namespace gr { + namespace blocks { + + class BLOCKS_API interleaved_char_to_complex_impl : public interleaved_char_to_complex + { + private: + bool d_vector_input; + public: + interleaved_char_to_complex_impl(bool vector_input=false); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + + +#endif /* INCLUDED_INTERLEAVED_CHAR_TO_COMPLEX_IMPL_H */ diff --git a/gr-blocks/lib/tag_debug_impl.cc b/gr-blocks/lib/tag_debug_impl.cc index 56f701b9d7..567f83dcb6 100644 --- a/gr-blocks/lib/tag_debug_impl.cc +++ b/gr-blocks/lib/tag_debug_impl.cc @@ -60,10 +60,17 @@ namespace gr { return d_tags; } + int + tag_debug_impl::num_tags() + { + std::vector<tag_t> t; + get_tags_in_range(t, 0, 0, nitems_read(0)); + return static_cast<int>(t.size()); + } + void tag_debug_impl::set_display(bool d) { - gr::thread::scoped_lock l(d_mutex); d_display = d; } @@ -114,5 +121,19 @@ namespace gr { return noutput_items; } + void + tag_debug_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<tag_debug, int>( + alias(), "num. tags", + &tag_debug::num_tags, + pmt::from_long(0), pmt::from_long(10000), pmt::from_long(0), + "", "Number of Tags", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); +#endif /* GR_CTRLPORT */ + } + } /* namespace blocks */ } /* namespace gr */ diff --git a/gr-blocks/lib/tag_debug_impl.h b/gr-blocks/lib/tag_debug_impl.h index 80d714edc2..03b1846139 100644 --- a/gr-blocks/lib/tag_debug_impl.h +++ b/gr-blocks/lib/tag_debug_impl.h @@ -43,7 +43,10 @@ namespace gr { tag_debug_impl(size_t sizeof_stream_item, const std::string &name); ~tag_debug_impl(); + void setup_rpc(); + std::vector<tag_t> current_tags(); + int num_tags(); void set_display(bool d); diff --git a/gr-blocks/lib/tags_strobe_impl.cc b/gr-blocks/lib/tags_strobe_impl.cc new file mode 100644 index 0000000000..41329d88a3 --- /dev/null +++ b/gr-blocks/lib/tags_strobe_impl.cc @@ -0,0 +1,99 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "tags_strobe_impl.h" +#include <gnuradio/io_signature.h> +#include <cstdio> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdexcept> +#include <string.h> +#include <iostream> + +namespace gr { + namespace blocks { + + tags_strobe::sptr + tags_strobe::make(size_t sizeof_stream_item, + pmt::pmt_t value, uint64_t nsamps) + { + return gnuradio::get_initial_sptr + (new tags_strobe_impl(sizeof_stream_item, value, nsamps)); + } + + tags_strobe_impl::tags_strobe_impl(size_t sizeof_stream_item, + pmt::pmt_t value, uint64_t nsamps) + : sync_block("tags_strobe", + io_signature::make(0, 0, 0), + io_signature::make(1, 1, sizeof_stream_item)), + d_itemsize(sizeof_stream_item) + { + set_value(value); + set_nsamps(nsamps); + } + + tags_strobe_impl::~tags_strobe_impl() + { + } + + void + tags_strobe_impl::set_value(pmt::pmt_t value) + { + d_tag.offset = 0; + d_tag.key = pmt::intern("strobe"); + d_tag.value = value; + d_tag.srcid = alias_pmt(); + } + + void + tags_strobe_impl::set_nsamps(uint64_t nsamps) + { + d_nsamps = nsamps; + d_offset = 0; + } + + int + tags_strobe_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + void *optr = (void*)output_items[0]; + memset(optr, 0, noutput_items * d_itemsize); + + uint64_t nitems = static_cast<uint64_t>(noutput_items) + nitems_written(0); + while((nitems - d_offset) > d_nsamps) { + d_offset += d_nsamps; + d_tag.offset = d_offset; + add_item_tag(0, d_tag); + } + + return noutput_items; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/tags_strobe_impl.h b/gr-blocks/lib/tags_strobe_impl.h new file mode 100644 index 0000000000..a6d73cfead --- /dev/null +++ b/gr-blocks/lib/tags_strobe_impl.h @@ -0,0 +1,59 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_TAGS_STROBE_IMPL_H +#define INCLUDED_GR_TAGS_STROBE_IMPL_H + +#include <gnuradio/blocks/tags_strobe.h> + +namespace gr { + namespace blocks { + + class BLOCKS_API tags_strobe_impl : public tags_strobe + { + private: + size_t d_itemsize; + uint64_t d_nsamps; + tag_t d_tag; + uint64_t d_offset; + + void run(); + + public: + tags_strobe_impl(size_t sizeof_stream_item, + pmt::pmt_t value, uint64_t nsamps); + ~tags_strobe_impl(); + + void set_value(pmt::pmt_t value); + pmt::pmt_t value() const { return d_tag.value; } + void set_nsamps(uint64_t nsamps); + uint64_t nsamps() const { return d_nsamps; } + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_TAGS_STROBE_IMPL_H */ diff --git a/gr-blocks/lib/vco_c_impl.cc b/gr-blocks/lib/vco_c_impl.cc new file mode 100644 index 0000000000..5103ce1851 --- /dev/null +++ b/gr-blocks/lib/vco_c_impl.cc @@ -0,0 +1,68 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "vco_c_impl.h" +#include <gnuradio/io_signature.h> +#include <math.h> + +namespace gr { + namespace blocks { + + vco_c::sptr + vco_c::make(double sampling_rate, double sensitivity, double amplitude) + { + return gnuradio::get_initial_sptr + (new vco_c_impl(sampling_rate, sensitivity, amplitude)); + } + + vco_c_impl::vco_c_impl(double sampling_rate, double sensitivity, double amplitude) + : sync_block("vco_c", + io_signature::make(1, 1, sizeof(float)), + io_signature::make(1, 1, sizeof(gr_complex))), + d_sampling_rate(sampling_rate), d_sensitivity(sensitivity), + d_amplitude(amplitude), d_k(d_sensitivity/d_sampling_rate) + { + } + + vco_c_impl::~vco_c_impl() + { + } + + int + vco_c_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + const float *input = (const float*)input_items[0]; + gr_complex *output = (gr_complex*)output_items[0]; + + d_vco.sincos(output, input, noutput_items, d_k, d_amplitude); + + return noutput_items; + } + + } /* namespace blocks */ +} /* namespace gr */ diff --git a/gr-blocks/lib/vco_c_impl.h b/gr-blocks/lib/vco_c_impl.h new file mode 100644 index 0000000000..53def4f1da --- /dev/null +++ b/gr-blocks/lib/vco_c_impl.h @@ -0,0 +1,53 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_VCO_F_IMPL_H +#define INCLUDED_GR_VCO_F_IMPL_H + +#include <gnuradio/blocks/vco_c.h> +#include <gnuradio/fxpt_vco.h> + +namespace gr { + namespace blocks { + + class vco_c_impl : public vco_c + { + private: + double d_sampling_rate; + double d_sensitivity; + double d_amplitude; + double d_k; + gr::fxpt_vco d_vco; + + public: + vco_c_impl(double sampling_rate, double sensitivity, double amplitude); + ~vco_c_impl(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace blocks */ +} /* namespace gr */ + +#endif /* INCLUDED_GR_VCO_C_H */ diff --git a/gr-blocks/python/blocks/CMakeLists.txt b/gr-blocks/python/blocks/CMakeLists.txt index 01e8d16587..9050eff6c2 100644 --- a/gr-blocks/python/blocks/CMakeLists.txt +++ b/gr-blocks/python/blocks/CMakeLists.txt @@ -48,6 +48,7 @@ if(ENABLE_TESTING) list(REMOVE_ITEM py_qa_test_files ${CMAKE_CURRENT_SOURCE_DIR}/qa_cpp_py_binding.py ${CMAKE_CURRENT_SOURCE_DIR}/qa_cpp_py_binding_set.py + ${CMAKE_CURRENT_SOURCE_DIR}/qa_ctrlport_probes.py ) endif(NOT ENABLE_GR_CTRLPORT) diff --git a/gr-blocks/python/blocks/qa_ctrlport_probes.py b/gr-blocks/python/blocks/qa_ctrlport_probes.py new file mode 100644 index 0000000000..b31934f705 --- /dev/null +++ b/gr-blocks/python/blocks/qa_ctrlport_probes.py @@ -0,0 +1,229 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +import Ice +import sys, time, random, numpy +from gnuradio import gr, gr_unittest, blocks + +from gnuradio.ctrlport import GNURadio +from gnuradio import ctrlport +import os, struct + +class test_ctrlport_probes(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + os.environ['GR_CONF_CONTROLPORT_ON'] = 'True' + + def tearDown(self): + self.tb = None + + def test_001(self): + data = range(1,9) + + self.src = blocks.vector_source_c(data, True) + self.probe = blocks.ctrlport_probe2_c("samples","Complex", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + # Probes return complex values as list of floats with re, im + # Imaginary parts of this data set are 0. + expected_result = [1, 0, 2, 0, 3, 0, 4, 0, + 5, 0, 6, 0, 7, 0, 8, 0] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + + + def test_002(self): + data = range(1,9) + + self.src = blocks.vector_source_f(data, True) + self.probe = blocks.ctrlport_probe2_f("samples","Floats", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + + + def test_003(self): + data = range(1,9) + + self.src = blocks.vector_source_i(data, True) + self.probe = blocks.ctrlport_probe2_i("samples","Integers", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + + + def test_004(self): + data = range(1,9) + + self.src = blocks.vector_source_s(data, True) + self.probe = blocks.ctrlport_probe2_s("samples","Shorts", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + i = result.index(1.0) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + + def test_005(self): + data = range(1,9) + + self.src = blocks.vector_source_b(data, True) + self.probe = blocks.ctrlport_probe2_b("samples","Bytes", + len(data), gr.DISPNULL) + probe_name = self.probe.alias() + + self.tb.connect(self.src, self.probe) + self.tb.start() + + expected_result = [1, 2, 3, 4, 5, 6, 7, 8,] + + # Make sure we have time for flowgraph to run + time.sleep(0.1) + + # Get available endpoint + ep = gr.rpcmanager_get().endpoints()[0] + + # Initialize a simple Ice client from endpoint + ic = Ice.initialize(sys.argv) + base = ic.stringToProxy(ep) + radio = GNURadio.ControlPortPrx.checkedCast(base) + + # Get all exported knobs + ret = radio.get([probe_name + "::samples"]) + for name in ret.keys(): + # Get data in probe, which might be offset; find the + # beginning and unwrap. + result = ret[name].value + result = list(struct.unpack(len(result)*'b', result)) + i = result.index(1) + result = result[i:] + result[0:i] + self.assertEqual(expected_result, result) + + self.tb.stop() + +if __name__ == '__main__': + gr_unittest.run(test_ctrlport_probes, "test_ctrlport_probes.xml") + diff --git a/gr-blocks/python/blocks/qa_hier_block2.py b/gr-blocks/python/blocks/qa_hier_block2.py index 51f420e8eb..5a351f2b37 100755 --- a/gr-blocks/python/blocks/qa_hier_block2.py +++ b/gr-blocks/python/blocks/qa_hier_block2.py @@ -393,5 +393,38 @@ class test_hier_block2(gr_unittest.TestCase): self.assertEquals(dst.data(), (3.0,)) + def test_033a_set_affinity(self): + expected = (1.0, 2.0, 3.0, 4.0) + hblock = gr.top_block("test_block") + src = blocks.vector_source_f(expected, False) + snk = blocks.vector_sink_f() + hblock.connect(src, snk) + hblock.set_processor_affinity([0,]) + hblock.run() + actual = snk.data() + self.assertEquals(expected, actual) + + def test_033b_unset_affinity(self): + expected = (1.0, 2.0, 3.0, 4.0) + hblock = gr.top_block("test_block") + src = blocks.vector_source_f(expected, False) + snk = blocks.vector_sink_f() + hblock.connect(src, snk) + hblock.set_processor_affinity([0,]) + hblock.unset_processor_affinity() + hblock.run() + actual = snk.data() + self.assertEquals(expected, actual) + + def test_033c_get_affinity(self): + expected = (1.0, 2.0, 3.0, 4.0) + hblock = gr.top_block("test_block") + src = blocks.vector_source_f(expected, False) + snk = blocks.vector_sink_f() + hblock.connect(src, snk) + hblock.set_processor_affinity([0,]) + procs = hblock.processor_affinity() + self.assertEquals((0,), procs) + if __name__ == "__main__": gr_unittest.run(test_hier_block2, "test_hier_block2.xml") diff --git a/gr-blocks/python/blocks/qa_tags_strobe.py b/gr-blocks/python/blocks/qa_tags_strobe.py new file mode 100644 index 0000000000..51b1c63bad --- /dev/null +++ b/gr-blocks/python/blocks/qa_tags_strobe.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python +# +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +from gnuradio import gr, gr_unittest, blocks +import pmt +import math + +class test_tags_strobe(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001(self): + N = 10000 + nsamps = 1000 + ntags = N / nsamps - 1 + + src = blocks.tags_strobe(gr.sizeof_float, + pmt.intern("TEST"), nsamps) + hed = blocks.head(gr.sizeof_float, N) + dst = blocks.vector_sink_f() + + self.tb.connect(src, hed, dst) + self.tb.run() + + self.assertEqual(ntags, len(dst.tags())) + + n_expected = nsamps + for tag in dst.tags(): + self.assertEqual(tag.offset, n_expected) + n_expected += nsamps + + def test_002(self): + N = 10000 + nsamps = 123 + ntags = N / nsamps + + src = blocks.tags_strobe(gr.sizeof_float, + pmt.intern("TEST"), nsamps) + hed = blocks.head(gr.sizeof_float, N) + dst = blocks.vector_sink_f() + + self.tb.connect(src, hed, dst) + self.tb.run() + + self.assertEqual(ntags, len(dst.tags())) + + n_expected = nsamps + for tag in dst.tags(): + self.assertEqual(tag.offset, n_expected) + n_expected += nsamps + + def test_003(self): + N = 100000 + nsamps = 10000 + ntags = N / nsamps - 1 + + src = blocks.tags_strobe(gr.sizeof_float, + pmt.intern("TEST"), nsamps) + hed = blocks.head(gr.sizeof_float, N) + dst = blocks.vector_sink_f() + + self.tb.connect(src, hed, dst) + self.tb.run() + + self.assertEqual(ntags, len(dst.tags())) + + n_expected = nsamps + for tag in dst.tags(): + self.assertEqual(tag.offset, n_expected) + n_expected += nsamps + +if __name__ == '__main__': + gr_unittest.run(test_tags_strobe, "test_tags_strobe.xml") diff --git a/gr-blocks/python/blocks/qa_vco.py b/gr-blocks/python/blocks/qa_vco.py index 4ed2a71c04..fdd1eb1001 100644 --- a/gr-blocks/python/blocks/qa_vco.py +++ b/gr-blocks/python/blocks/qa_vco.py @@ -28,6 +28,12 @@ def sig_source_f(samp_rate, freq, amp, N): y = map(lambda x: amp*math.cos(2.*math.pi*freq*x), t) return y +def sig_source_c(samp_rate, freq, amp, N): + t = map(lambda x: float(x)/samp_rate, xrange(N)) + y = map(lambda x: math.cos(2.*math.pi*freq*x) + \ + 1j*math.sin(2.*math.pi*freq*x), t) + return y + class test_vco(gr_unittest.TestCase): def setUp (self): @@ -53,6 +59,23 @@ class test_vco(gr_unittest.TestCase): self.assertFloatTuplesAlmostEqual(expected_result, result_data, 5) + def test_002(self): + src_data = 200*[0,] + 200*[0.5,] + 200*[1,] + expected_result = 200*[1,] + \ + sig_source_c(1, 0.125, 1, 200) + \ + sig_source_c(1, 0.25, 1, 200) + + src = blocks.vector_source_f(src_data) + op = blocks.vco_c(1, math.pi/2.0, 1) + dst = blocks.vector_sink_c() + + self.tb.connect(src, op, dst) + self.tb.run() + + result_data = dst.data() + self.assertComplexTuplesAlmostEqual(expected_result, result_data, 5) + + if __name__ == '__main__': gr_unittest.run(test_vco, "test_vco.xml") diff --git a/gr-blocks/swig/blocks_swig0.i b/gr-blocks/swig/blocks_swig0.i index ec5b75b0d1..28e14fea35 100644 --- a/gr-blocks/swig/blocks_swig0.i +++ b/gr-blocks/swig/blocks_swig0.i @@ -103,12 +103,24 @@ GR_SWIG_BLOCK_MAGIC2(blocks, null_source); %{ #include "gnuradio/blocks/ctrlport_probe_c.h" #include "gnuradio/blocks/ctrlport_probe2_c.h" +#include "gnuradio/blocks/ctrlport_probe2_f.h" +#include "gnuradio/blocks/ctrlport_probe2_s.h" +#include "gnuradio/blocks/ctrlport_probe2_i.h" +#include "gnuradio/blocks/ctrlport_probe2_b.h" %} %include "gnuradio/blocks/ctrlport_probe_c.h" %include "gnuradio/blocks/ctrlport_probe2_c.h" +%include "gnuradio/blocks/ctrlport_probe2_f.h" +%include "gnuradio/blocks/ctrlport_probe2_s.h" +%include "gnuradio/blocks/ctrlport_probe2_i.h" +%include "gnuradio/blocks/ctrlport_probe2_b.h" GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe_c); GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe2_c); +GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe2_f); +GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe2_s); +GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe2_i); +GR_SWIG_BLOCK_MAGIC2(blocks, ctrlport_probe2_b); #endif /* GR_CTRLPORT */ diff --git a/gr-blocks/swig/blocks_swig3.i b/gr-blocks/swig/blocks_swig3.i index 7e7e8bf365..ab921a559a 100644 --- a/gr-blocks/swig/blocks_swig3.i +++ b/gr-blocks/swig/blocks_swig3.i @@ -40,6 +40,7 @@ #include "gnuradio/blocks/integrate_cc.h" #include "gnuradio/blocks/interleave.h" #include "gnuradio/blocks/interleaved_short_to_complex.h" +#include "gnuradio/blocks/interleaved_char_to_complex.h" #include "gnuradio/blocks/keep_m_in_n.h" #include "gnuradio/blocks/keep_one_in_n.h" #include "gnuradio/blocks/lfsr_32k_source_s.h" @@ -81,6 +82,7 @@ %include "gnuradio/blocks/integrate_cc.h" %include "gnuradio/blocks/interleave.h" %include "gnuradio/blocks/interleaved_short_to_complex.h" +%include "gnuradio/blocks/interleaved_char_to_complex.h" %include "gnuradio/blocks/keep_m_in_n.h" %include "gnuradio/blocks/keep_one_in_n.h" %include "gnuradio/blocks/lfsr_32k_source_s.h" @@ -121,6 +123,7 @@ GR_SWIG_BLOCK_MAGIC2(blocks, integrate_ff); GR_SWIG_BLOCK_MAGIC2(blocks, integrate_cc); GR_SWIG_BLOCK_MAGIC2(blocks, interleave); GR_SWIG_BLOCK_MAGIC2(blocks, interleaved_short_to_complex); +GR_SWIG_BLOCK_MAGIC2(blocks, interleaved_char_to_complex); GR_SWIG_BLOCK_MAGIC2(blocks, keep_m_in_n); GR_SWIG_BLOCK_MAGIC2(blocks, keep_one_in_n); GR_SWIG_BLOCK_MAGIC2(blocks, lfsr_32k_source_s); diff --git a/gr-blocks/swig/blocks_swig5.i b/gr-blocks/swig/blocks_swig5.i index 1b99720be8..09679531d9 100644 --- a/gr-blocks/swig/blocks_swig5.i +++ b/gr-blocks/swig/blocks_swig5.i @@ -52,6 +52,7 @@ #include "gnuradio/blocks/tag_gate.h" #include "gnuradio/blocks/tagged_stream_mux.h" #include "gnuradio/blocks/tagged_stream_to_pdu.h" +#include "gnuradio/blocks/tags_strobe.h" #include "gnuradio/blocks/threshold_ff.h" #include "gnuradio/blocks/transcendental.h" #include "gnuradio/blocks/tuntap_pdu.h" @@ -63,6 +64,7 @@ #include "gnuradio/blocks/unpacked_to_packed_ss.h" #include "gnuradio/blocks/unpacked_to_packed_ii.h" #include "gnuradio/blocks/vco_f.h" +#include "gnuradio/blocks/vco_c.h" #include "gnuradio/blocks/xor_bb.h" #include "gnuradio/blocks/xor_ss.h" #include "gnuradio/blocks/xor_ii.h" @@ -89,6 +91,7 @@ %include "gnuradio/blocks/tag_gate.h" %include "gnuradio/blocks/tagged_stream_mux.h" %include "gnuradio/blocks/tagged_stream_to_pdu.h" +%include "gnuradio/blocks/tags_strobe.h" %include "gnuradio/blocks/threshold_ff.h" %include "gnuradio/blocks/transcendental.h" %include "gnuradio/blocks/tuntap_pdu.h" @@ -100,6 +103,7 @@ %include "gnuradio/blocks/unpacked_to_packed_ss.h" %include "gnuradio/blocks/unpacked_to_packed_ii.h" %include "gnuradio/blocks/vco_f.h" +%include "gnuradio/blocks/vco_c.h" %include "gnuradio/blocks/xor_bb.h" %include "gnuradio/blocks/xor_ss.h" %include "gnuradio/blocks/xor_ii.h" @@ -125,6 +129,7 @@ GR_SWIG_BLOCK_MAGIC2(blocks, sub_cc); GR_SWIG_BLOCK_MAGIC2(blocks, tag_gate); GR_SWIG_BLOCK_MAGIC2(blocks, tagged_stream_mux); GR_SWIG_BLOCK_MAGIC2(blocks, tagged_stream_to_pdu); +GR_SWIG_BLOCK_MAGIC2(blocks, tags_strobe); GR_SWIG_BLOCK_MAGIC2(blocks, threshold_ff); GR_SWIG_BLOCK_MAGIC2(blocks, transcendental); GR_SWIG_BLOCK_MAGIC2(blocks, tuntap_pdu); @@ -136,6 +141,7 @@ GR_SWIG_BLOCK_MAGIC2(blocks, unpacked_to_packed_bb); GR_SWIG_BLOCK_MAGIC2(blocks, unpacked_to_packed_ss); GR_SWIG_BLOCK_MAGIC2(blocks, unpacked_to_packed_ii); GR_SWIG_BLOCK_MAGIC2(blocks, vco_f); +GR_SWIG_BLOCK_MAGIC2(blocks, vco_c); GR_SWIG_BLOCK_MAGIC2(blocks, xor_bb); GR_SWIG_BLOCK_MAGIC2(blocks, xor_ss); GR_SWIG_BLOCK_MAGIC2(blocks, xor_ii); diff --git a/gr-channels/CMakeLists.txt b/gr-channels/CMakeLists.txt index f8e46fd207..2b5b4cd7d5 100644 --- a/gr-channels/CMakeLists.txt +++ b/gr-channels/CMakeLists.txt @@ -42,6 +42,8 @@ GR_SET_GLOBAL(GR_CHANNELS_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include ) +SET(GR_PKG_CHANNELS_EXAMPLES_DIR ${GR_PKG_DATA_DIR}/examples/channels) + ######################################################################## # Begin conditional configuration ######################################################################## @@ -90,8 +92,8 @@ if(ENABLE_PYTHON) add_subdirectory(swig) add_subdirectory(python/channels) add_subdirectory(grc) + add_subdirectory(examples) endif(ENABLE_PYTHON) -#add_subdirectory(examples) add_subdirectory(doc) ######################################################################## diff --git a/gr-channels/doc/channels.dox b/gr-channels/doc/channels.dox index c4440b45b0..05d51b9ba2 100644 --- a/gr-channels/doc/channels.dox +++ b/gr-channels/doc/channels.dox @@ -20,4 +20,114 @@ after importing by using: help(channels) \endcode + +\section channels_types Available Channel Models + +\subsection channels_awgn AWGN Channel + +The basic gr::channels::channel_model block is essentially an +additive white Gaussian noise (AWGN) channel with a few extra +additions. This channel model simulates AWGN as well as frequency and +timing offsets between the transmit and receiver and a simple static +multipath environment. + +The parameters include: + +\li noise_voltage: The AWGN noise level as a voltage (to be calculated + externally to meet, say, a desired SNR). +\li frequency_offset The normalized frequency offset. 0 is no offset; + 0.25 would be, for a digital modem, one quarter of the symbol + rate. +\li epsilon The sample timing offset to emulate the different rates + between the sample clocks of the transmitter and receiver. 1.0 is + no difference. +\li taps Taps of a FIR filter to emulate a multipath delay profile. +\li noise_seed A random number generator seed for the noise source. + + +\subsection channels_fading Fading Channel Model + +The gr::channels::fading_model builds a basic fading simulation. This +model approximates a fading channel using the sum of sinusoids method +for the number of expected multipath components. This block also takes +in the Doppler frequency shift as a normalized value, a line-of-sight +(LOS) parameter that is either true or false, the Rician K factor, and +a random seed to the noise generators. These parameters are provided +in the documentation as: + +\li N The number of sinusiods to use in simulating the channel 8 is a good value +\li fDTs normalized maximum Doppler frequency, fD * Ts +\li LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models +\li K Rician factor (ratio of the specular power to the scattered power) +\li seed a random number to seed the noise generators + + +\subsection channels_selective_fading Frequency-Selective Fading Model + +Extending the fading model, the gr::channels::selective_fading_model +channel model allows us to specify frequency selective parameters for +the model. Starting with the same inputs as the +gr::channels::fading_model, this block also takes in power delay +profile (PDP). The PDP is given as a vector of the times of the delays +and another vector of the corresponding magnitudes of the PDP. We also +specify the number of taps in the filter to simulate this. These +parameters are provided in the documentation as: + +\li N The number of sinusiods to use in simulating the channel 8 is a good value +\li fDTs normalized maximum Doppler frequency, fD * Ts +\li LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models +\li K Rician factor (ratio of the specular power to the scattered power) +\li seed a random number to seed the noise generators +\li delays A vector of values the specify the time delay of each impulse +\li mags A vector of values that specifies the magnitude of each impulse +\li ntaps The number of filter taps. + + +\subsection channels_hw_impairments Hardware Impairments Model + +GNU Radio also provides a model for incorporating parameters of a +radio transmitter and receiver to better simulate signals as they +would be transmitted and received through real hardware. This +simulation tool can be used to see how a waveform would behave over a +known radio, or if designing radio hardware, this block could help +understand the requirements and tolerances for the new system. + +The hardware impairments model is a Python-only hierarchical block, +which is declared as digital.impairments(...). The block is also +represented in GRC under the "Impairment Models" category. + +The impairments model is actually made up of a handful of other +hierarchical blocks, each of which perform one aspect of the +impairments model. Each subsection is usable on its own, and each one +can be used in GRC. These subsystems are: + +\li digital.phase_noise_gen ("Phase Noise Generator") +\li digital.iqbal_gen ("IQ Imbalance Generator") +\li digital.distortion_3_gen ("Third Order Distortion") +\li digital.distortion_2_gen ("Second Order Distortion") + +There are also a few other blocks that were built to develop and +simulate different hardware behaviors + +\li digital.quantizer ("Quantizer"): quantizes to b number of bits. +\li digital.amp_bal ("Amplitude Balance"): corrects IQ amplitude imbalance. +\li digital.phase_bal ("Phase Balance"): corrects IQ phase imbalance. + +The digital.impairments model combines the phase noise, IQ imbalance, +and second and third order distortion generators. It also performs I +and Q offset imbalance and internal frequency shifting problems. + +The parameters for using the hardware impairments model are: + +\li phase_noise_mag: the level of the phase noise (in dB). +\li magbal: The level of the magnitude imbalance between I and Q. +\li phasebal: The level of the phase imbalance (as an angle). +\li q_ofs: Offset value of the quadrature arm (as a magnitude). +\li i_ofs: Offset value of the in-phase arm (as a magnitude). +\li freq_offset: normalized frequency offset. +\li gamma: Level of the 2nd order distortion. +\li beta: level of the 3rd order distortion. + + + */ diff --git a/gr-channels/examples/CMakeLists.txt b/gr-channels/examples/CMakeLists.txt new file mode 100644 index 0000000000..af33a8f66f --- /dev/null +++ b/gr-channels/examples/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2013 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +include(GrPython) + +install( + FILES + channel_tone_response.grc + demo_quantization.grc + demo_two_tone.grc + demo_spec_an.grc + demo_gmsk.grc + demo_qam.grc + demo_ofdm.grc + DESTINATION ${GR_PKG_CHANNELS_EXAMPLES_DIR} + COMPONENT "channels_python" +) diff --git a/gr-channels/examples/demo_gmsk.grc b/gr-channels/examples/demo_gmsk.grc new file mode 100644 index 0000000000..a5531fb46b --- /dev/null +++ b/gr-channels/examples/demo_gmsk.grc @@ -0,0 +1,894 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Thu Aug 1 13:51:27 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_gmsk</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>const_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4</value> + </param> + <param> + <key>_coordinate</key> + <value>(9, 89)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(98, 90)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 152)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_random_source_x</key> + <param> + <key>id</key> + <value>analog_random_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>byte</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>256</value> + </param> + <param> + <key>num_samps</key> + <value>100000</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(13, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>digital_gmsk_mod</key> + <param> + <key>id</key> + <value>digital_gmsk_mod_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>samples_per_symbol</key> + <value>10</value> + </param> + <param> + <key>bt</key> + <value>0.3</value> + </param> + <param> + <key>verbose</key> + <value>False</value> + </param> + <param> + <key>log</key> + <value>False</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(214, 219)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_impairments</key> + <param> + <key>id</key> + <value>channels_impairments_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>phase_noise_mag</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>magbal</key> + <value>magbal</value> + </param> + <param> + <key>phasebal</key> + <value>phasebal</value> + </param> + <param> + <key>q_ofs</key> + <value>q_ofs</value> + </param> + <param> + <key>i_ofs</key> + <value>i_ofs</value> + </param> + <param> + <key>freq_offset</key> + <value>freq_offset</value> + </param> + <param> + <key>gamma</key> + <value>0</value> + </param> + <param> + <key>beta</key> + <value>beta</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(451, 195)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>ymin</key> + <value>-1</value> + </param> + <param> + <key>ymax</key> + <value>1</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,1,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(916, 235)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_quadrature_demod_cf</key> + <param> + <key>id</key> + <value>analog_quadrature_demod_cf_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>gain</key> + <value>1.0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(708, 251)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_const_sink_x</key> + <param> + <key>id</key> + <value>qtgui_const_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>xmin</key> + <value>-2</value> + </param> + <param> + <key>xmax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,0,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(706, 176)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(227, 310)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>q_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Q offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(933, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>i_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>I offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(826, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phasebal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Phase Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>45</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(672, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>magbal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Magnitude Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>10</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(494, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>freq_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Frequency Offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-0.5</value> + </param> + <param> + <key>stop</key> + <value>0.5</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(354, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase Noise Mag (dB)</value> + </param> + <param> + <key>value</key> + <value>-100</value> + </param> + <param> + <key>start</key> + <value>-100</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0, 0, 1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(191, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>beta</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>k3 (IP3)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-.1</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(1041, 12)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>analog_random_source_x_0</source_block_id> + <sink_block_id>digital_gmsk_mod_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_impairments_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>digital_gmsk_mod_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>qtgui_const_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_quadrature_demod_cf_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>analog_quadrature_demod_cf_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/examples/demo_ofdm.grc b/gr-channels/examples/demo_ofdm.grc new file mode 100644 index 0000000000..f3c3a80e0e --- /dev/null +++ b/gr-channels/examples/demo_ofdm.grc @@ -0,0 +1,1410 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Thu Aug 1 15:11:44 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_ofdm</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>const_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>64</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 89)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>fft_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>1024</value> + </param> + <param> + <key>_coordinate</key> + <value>(99, 89)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 151)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>k</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>math.log(const_size)/math.log(2)</value> + </param> + <param> + <key>_coordinate</key> + <value>(100, 151)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(168, 153)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>q_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Q offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(936, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>i_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>I offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(829, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phasebal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Phase Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>45</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(675, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>magbal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Magnitude Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>10</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(497, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>freq_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Frequency Offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-0.5</value> + </param> + <param> + <key>stop</key> + <value>0.5</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(357, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase Noise Mag (dB)</value> + </param> + <param> + <key>value</key> + <value>-100</value> + </param> + <param> + <key>start</key> + <value>-100</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0, 0, 1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(194, 7)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>ymin</key> + <value>-1</value> + </param> + <param> + <key>ymax</key> + <value>1</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,1,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1102, 249)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>beta</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>k3 (IP3)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-10</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>0.01</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(1042, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_complex</key> + <param> + <key>id</key> + <value>blocks_float_to_complex_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(237, 424)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fft_vxx</key> + <param> + <key>id</key> + <value>fft_fft_vxx_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>forward</key> + <value>True</value> + </param> + <param> + <key>window</key> + <value>fft_size*[1.0/math.sqrt(fft_size)]</value> + </param> + <param> + <key>shift</key> + <value>False</value> + </param> + <param> + <key>nthreads</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(689, 150)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>fft_vxx</key> + <param> + <key>id</key> + <value>fft_fft_vxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>forward</key> + <value>False</value> + </param> + <param> + <key>window</key> + <value>fft_size*[1.0/math.sqrt(fft_size)]</value> + </param> + <param> + <key>shift</key> + <value>False</value> + </param> + <param> + <key>nthreads</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(565, 485)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_multiply_const_vxx</key> + <param> + <key>id</key> + <value>blocks_multiply_const_vxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>const</key> + <value>1.0/fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(854, 571)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_vector_to_stream</key> + <param> + <key>id</key> + <value>blocks_vector_to_stream_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_items</key> + <value>fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(833, 468)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_impairments</key> + <param> + <key>id</key> + <value>channels_impairments_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>phase_noise_mag</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>magbal</key> + <value>magbal</value> + </param> + <param> + <key>phasebal</key> + <value>phasebal</value> + </param> + <param> + <key>q_ofs</key> + <value>q_ofs</value> + </param> + <param> + <key>i_ofs</key> + <value>i_ofs</value> + </param> + <param> + <key>freq_offset</key> + <value>freq_offset</value> + </param> + <param> + <key>gamma</key> + <value>0</value> + </param> + <param> + <key>beta</key> + <value>beta</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(1074, 460)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_stream_to_vector</key> + <param> + <key>id</key> + <value>blocks_stream_to_vector_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_items</key> + <value>fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(889, 400)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + </block> + <block> + <key>blocks_vector_to_stream</key> + <param> + <key>id</key> + <value>blocks_vector_to_stream_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_items</key> + <value>fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(806, 315)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_stream_to_vector</key> + <param> + <key>id</key> + <value>blocks_stream_to_vector_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_items</key> + <value>fft_size</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(449, 343)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + </block> + <block> + <key>blocks_add_const_vxx</key> + <param> + <key>id</key> + <value>blocks_add_const_vxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>const</key> + <value>(-1-1j)/2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(337, 175)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(438, 243)</value> + </param> + <param> + <key>_rotation</key> + <value>180</value> + </param> + </block> + <block> + <key>qtgui_const_sink_x</key> + <param> + <key>id</key> + <value>qtgui_const_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>xmin</key> + <value>-2</value> + </param> + <param> + <key>xmax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,0,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1102, 175)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_int_to_float</key> + <param> + <key>id</key> + <value>blocks_int_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>k-1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(63, 412)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_int_to_float</key> + <param> + <key>id</key> + <value>blocks_int_to_float_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>k-1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(63, 462)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_random_source_x</key> + <param> + <key>id</key> + <value>analog_random_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>int</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>int(k)</value> + </param> + <param> + <key>num_samps</key> + <value>10000</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(22, 271)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_random_source_x</key> + <param> + <key>id</key> + <value>analog_random_source_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>int</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>int(k)</value> + </param> + <param> + <key>num_samps</key> + <value>10000</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(36, 543)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_stream_to_vector_0_0</source_block_id> + <sink_block_id>fft_fft_vxx_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>blocks_stream_to_vector_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>blocks_stream_to_vector_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_fft_vxx_0_0</source_block_id> + <sink_block_id>blocks_vector_to_stream_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_add_const_vxx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_complex_0</source_block_id> + <sink_block_id>blocks_add_const_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_random_source_x_0</source_block_id> + <sink_block_id>blocks_int_to_float_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_int_to_float_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_int_to_float_0_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>analog_random_source_x_0_0</source_block_id> + <sink_block_id>blocks_int_to_float_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_to_stream_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_to_stream_0</source_block_id> + <sink_block_id>qtgui_const_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>fft_fft_vxx_0</source_block_id> + <sink_block_id>blocks_vector_to_stream_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_vector_to_stream_0_0</source_block_id> + <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_multiply_const_vxx_0</source_block_id> + <sink_block_id>channels_impairments_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_stream_to_vector_0</source_block_id> + <sink_block_id>fft_fft_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/examples/demo_qam.grc b/gr-channels/examples/demo_qam.grc new file mode 100644 index 0000000000..d4c56d7d1a --- /dev/null +++ b/gr-channels/examples/demo_qam.grc @@ -0,0 +1,1048 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Thu Aug 1 15:09:47 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_qam</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(97, 91)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>const_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>16</value> + </param> + <param> + <key>_coordinate</key> + <value>(9, 90)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>k</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>math.log(const_size)/math.log(2)</value> + </param> + <param> + <key>_coordinate</key> + <value>(101, 153)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>q_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Q offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(943, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>i_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>I offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(836, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>magbal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Magnitude Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>10</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(504, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phasebal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Phase Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>45</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(682, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>freq_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Frequency Offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-0.5</value> + </param> + <param> + <key>stop</key> + <value>0.5</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(364, 18)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase Noise Mag (dB)</value> + </param> + <param> + <key>value</key> + <value>-100</value> + </param> + <param> + <key>start</key> + <value>-100</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0, 0, 1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(201, 17)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 154)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_time_sink_x</key> + <param> + <key>id</key> + <value>qtgui_time_sink_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>srate</key> + <value>samp_rate</value> + </param> + <param> + <key>ymin</key> + <value>-1</value> + </param> + <param> + <key>ymax</key> + <value>1</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,1,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1038, 334)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_impairments</key> + <param> + <key>id</key> + <value>channels_impairments_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>phase_noise_mag</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>magbal</key> + <value>magbal</value> + </param> + <param> + <key>phasebal</key> + <value>phasebal</value> + </param> + <param> + <key>q_ofs</key> + <value>q_ofs</value> + </param> + <param> + <key>i_ofs</key> + <value>i_ofs</value> + </param> + <param> + <key>freq_offset</key> + <value>freq_offset</value> + </param> + <param> + <key>gamma</key> + <value>0</value> + </param> + <param> + <key>beta</key> + <value>beta</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(776, 251)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_float_to_complex</key> + <param> + <key>id</key> + <value>blocks_float_to_complex_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(374, 294)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(593, 307)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_const_sink_x</key> + <param> + <key>id</key> + <value>qtgui_const_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>1024</value> + </param> + <param> + <key>ymin</key> + <value>-2</value> + </param> + <param> + <key>ymax</key> + <value>2</value> + </param> + <param> + <key>xmin</key> + <value>-2</value> + </param> + <param> + <key>xmax</key> + <value>2</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>4,0,1,1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1038, 261)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>beta</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>k3 (IP3)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(1051, 19)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_random_source_x</key> + <param> + <key>id</key> + <value>analog_random_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>int</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>int(k)</value> + </param> + <param> + <key>num_samps</key> + <value>10000</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(13, 239)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_random_source_x</key> + <param> + <key>id</key> + <value>analog_random_source_x_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>int</value> + </param> + <param> + <key>min</key> + <value>0</value> + </param> + <param> + <key>max</key> + <value>int(k)</value> + </param> + <param> + <key>num_samps</key> + <value>10000</value> + </param> + <param> + <key>repeat</key> + <value>True</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(13, 332)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_int_to_float</key> + <param> + <key>id</key> + <value>blocks_int_to_float_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>k-1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(190, 263)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_int_to_float</key> + <param> + <key>id</key> + <value>blocks_int_to_float_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>scale</key> + <value>k-1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(191, 356)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_add_const_vxx</key> + <param> + <key>id</key> + <value>blocks_add_const_vxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>const</key> + <value>(-1-1j)/2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(402, 396)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>qtgui_const_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>qtgui_time_sink_x_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_impairments_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_random_source_x_0_0</source_block_id> + <sink_block_id>blocks_int_to_float_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_int_to_float_0_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_int_to_float_0</source_block_id> + <sink_block_id>blocks_float_to_complex_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_add_const_vxx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_float_to_complex_0</source_block_id> + <sink_block_id>blocks_add_const_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_random_source_x_0</source_block_id> + <sink_block_id>blocks_int_to_float_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/examples/demo_quantization.grc b/gr-channels/examples/demo_quantization.grc new file mode 100644 index 0000000000..134e224e7f --- /dev/null +++ b/gr-channels/examples/demo_quantization.grc @@ -0,0 +1,958 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Fri Aug 2 16:57:39 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_quantization</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100000</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 134)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 88)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>bw</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>BW</value> + </param> + <param> + <key>value</key> + <value>samp_rate/2</value> + </param> + <param> + <key>start</key> + <value>samp_rate/100</value> + </param> + <param> + <key>stop</key> + <value>samp_rate/2</value> + </param> + <param> + <key>step</key> + <value>100</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,2,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(873, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>center</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Center Freq.</value> + </param> + <param> + <key>value</key> + <value>samp_rate/11.0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>samp_rate/2</value> + </param> + <param> + <key>step</key> + <value>100</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(724, 11)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_chooser</key> + <param> + <key>id</key> + <value>bits</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Bits</value> + </param> + <param> + <key>type</key> + <value>int</value> + </param> + <param> + <key>num_opts</key> + <value>0</value> + </param> + <param> + <key>value</key> + <value>16</value> + </param> + <param> + <key>options</key> + <value>[0, 1, 2, 4, 6, 8, 10, 12, 14, 16]</value> + </param> + <param> + <key>labels</key> + <value>[]</value> + </param> + <param> + <key>option0</key> + <value>0</value> + </param> + <param> + <key>label0</key> + <value></value> + </param> + <param> + <key>option1</key> + <value>1</value> + </param> + <param> + <key>label1</key> + <value></value> + </param> + <param> + <key>option2</key> + <value>2</value> + </param> + <param> + <key>label2</key> + <value></value> + </param> + <param> + <key>option3</key> + <value>3</value> + </param> + <param> + <key>label3</key> + <value></value> + </param> + <param> + <key>option4</key> + <value>4</value> + </param> + <param> + <key>label4</key> + <value></value> + </param> + <param> + <key>widget</key> + <value>combo_box</value> + </param> + <param> + <key>orient</key> + <value>Qt.QVBoxLayout</value> + </param> + <param> + <key>gui_hint</key> + <value>1,2,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(570, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>sigfreq</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Signal Freq</value> + </param> + <param> + <key>value</key> + <value>samp_rate*1.0247385/11.0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>samp_rate/2</value> + </param> + <param> + <key>step</key> + <value>100</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(427, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>signal_amp</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Signal Power</value> + </param> + <param> + <key>value</key> + <value>-150</value> + </param> + <param> + <key>start</key> + <value>-150</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0,2,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(301, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>noise_amp</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Noise Power</value> + </param> + <param> + <key>value</key> + <value>-150</value> + </param> + <param> + <key>start</key> + <value>-150</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>0,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(173, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_add_xx</key> + <param> + <key>id</key> + <value>blocks_add_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(489, 297)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_quantizer</key> + <param> + <key>id</key> + <value>channels_quantizer_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>bits</key> + <value>bits</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(827, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_multiply_const_vxx</key> + <param> + <key>id</key> + <value>blocks_multiply_const_vxx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>const</key> + <value>pow(2,bits-1)</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(800, 287)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_freq_sink_x</key> + <param> + <key>id</key> + <value>qtgui_freq_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>fftsize</key> + <value>2048</value> + </param> + <param> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + </param> + <param> + <key>fc</key> + <value>0</value> + </param> + <param> + <key>bw</key> + <value>samp_rate</value> + </param> + <param> + <key>ymin</key> + <value>-140</value> + </param> + <param> + <key>ymax</key> + <value>10</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>3,1,1,2</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(998, 170)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> + </param> + <param> + <key>freq</key> + <value>sigfreq</value> + </param> + <param> + <key>amp</key> + <value>pow(10.0,signal_amp/20.0)</value> + </param> + <param> + <key>offset</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(7, 295)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>band_pass_filter</key> + <param> + <key>id</key> + <value>band_pass_filter_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>fir_filter_fff</value> + </param> + <param> + <key>decim</key> + <value>1</value> + </param> + <param> + <key>interp</key> + <value>1</value> + </param> + <param> + <key>gain</key> + <value>1</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>low_cutoff_freq</key> + <value>max(bw/15.0,center-bw/2.0)</value> + </param> + <param> + <key>high_cutoff_freq</key> + <value>min(samp_rate/2.0-bw/15.0,center+bw/2.0)</value> + </param> + <param> + <key>width</key> + <value>bw/5.0</value> + </param> + <param> + <key>win</key> + <value>firdes.WIN_HANN</value> + </param> + <param> + <key>beta</key> + <value>6.76</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(212, 173)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_noise_source_x</key> + <param> + <key>id</key> + <value>analog_noise_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>noise_type</key> + <value>analog.GR_GAUSSIAN</value> + </param> + <param> + <key>amp</key> + <value>pow(10.0,noise_amp/20.0)</value> + </param> + <param> + <key>seed</key> + <value>42</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(9, 213)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(634, 203)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_histogram_sink_x</key> + <param> + <key>id</key> + <value>qtgui_histogram_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>size</key> + <value>10240</value> + </param> + <param> + <key>bins</key> + <value>1000</value> + </param> + <param> + <key>xmin</key> + <value>-10000</value> + </param> + <param> + <key>xmax</key> + <value>10000</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.1</value> + </param> + <param> + <key>gui_hint</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(998, 271)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_quantizer_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_quantizer_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>band_pass_filter_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_add_xx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_quantizer_0</source_block_id> + <sink_block_id>blocks_multiply_const_vxx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_multiply_const_vxx_0</source_block_id> + <sink_block_id>qtgui_histogram_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>analog_noise_source_x_0</source_block_id> + <sink_block_id>band_pass_filter_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/examples/demo_spec_an.grc b/gr-channels/examples/demo_spec_an.grc new file mode 100644 index 0000000000..572ded6e67 --- /dev/null +++ b/gr-channels/examples/demo_spec_an.grc @@ -0,0 +1,1087 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Thu Aug 1 13:13:07 2013</timestamp> + <block> + <key>blocks_add_xx</key> + <param> + <key>id</key> + <value>blocks_add_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(251, 344)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>fft_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>1000</value> + </param> + <param> + <key>_coordinate</key> + <value>(17, 84)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>100000</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 170)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>imp0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(238, 60)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_noise_source_x</key> + <param> + <key>id</key> + <value>analog_noise_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>noise_type</key> + <value>analog.GR_GAUSSIAN</value> + </param> + <param> + <key>amp</key> + <value>math.pow(10.0,snr/20.0)/math.sqrt(2.0)</value> + </param> + <param> + <key>seed</key> + <value>42</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 295)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>id</key> + <value>freq_offset</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Freq_offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>min</key> + <value>-5*samp_rate/fft_size</value> + </param> + <param> + <key>max</key> + <value>5*samp_rate/fft_size</value> + </param> + <param> + <key>num_steps</key> + <value>200</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(314, 514)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> + </param> + <param> + <key>freq</key> + <value>float(samp_rate)/float(fft_size)*26+freq_offset</value> + </param> + <param> + <key>amp</key> + <value>1.0</value> + </param> + <param> + <key>offset</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(16, 386)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(370, 357)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_slider</key> + <param> + <key>id</key> + <value>snr</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>"Noise Level"</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>min</key> + <value>-100</value> + </param> + <param> + <key>max</key> + <value>100</value> + </param> + <param> + <key>num_steps</key> + <value>200</value> + </param> + <param> + <key>style</key> + <value>wx.SL_HORIZONTAL</value> + </param> + <param> + <key>converver</key> + <value>float_converter</value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(167, 510)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_rms_xx</key> + <param> + <key>id</key> + <value>blocks_rms_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>alpha</key> + <value>.0001</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(491, 138)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_rms_xx</key> + <param> + <key>id</key> + <value>blocks_rms_xx_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>alpha</key> + <value>.0001</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(465, 458)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_divide_xx</key> + <param> + <key>id</key> + <value>blocks_divide_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>num_inputs</key> + <value>2</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(680, 630)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_nlog10_ff</key> + <param> + <key>id</key> + <value>blocks_nlog10_ff_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>n</key> + <value>20</value> + </param> + <param> + <key>k</key> + <value>0</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(813, 640)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>wxgui_numbersink2</key> + <param> + <key>id</key> + <value>snr_disp</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>title</key> + <value>SNR</value> + </param> + <param> + <key>units</key> + <value>dB</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>min_value</key> + <value>-100</value> + </param> + <param> + <key>max_value</key> + <value>100</value> + </param> + <param> + <key>factor</key> + <value>1.0</value> + </param> + <param> + <key>decimal_places</key> + <value>5</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>number_rate</key> + <value>15</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>avg_alpha</key> + <value>0</value> + </param> + <param> + <key>show_gauge</key> + <value>False</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(907, 654)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>wxgui_numbersink2</key> + <param> + <key>id</key> + <value>wxgui_numbersink2_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>title</key> + <value>Noise Amplitude</value> + </param> + <param> + <key>units</key> + <value></value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>min_value</key> + <value>-100</value> + </param> + <param> + <key>max_value</key> + <value>100</value> + </param> + <param> + <key>factor</key> + <value>1.0</value> + </param> + <param> + <key>decimal_places</key> + <value>5</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>number_rate</key> + <value>15</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>avg_alpha</key> + <value>0</value> + </param> + <param> + <key>show_gauge</key> + <value>False</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(774, 46)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>wxgui_numbersink2</key> + <param> + <key>id</key> + <value>wxgui_numbersink2_0_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>float</value> + </param> + <param> + <key>title</key> + <value>Signal Amplitude</value> + </param> + <param> + <key>units</key> + <value></value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>min_value</key> + <value>-100</value> + </param> + <param> + <key>max_value</key> + <value>100</value> + </param> + <param> + <key>factor</key> + <value>1.0</value> + </param> + <param> + <key>decimal_places</key> + <value>5</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>number_rate</key> + <value>15</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>avg_alpha</key> + <value>0</value> + </param> + <param> + <key>show_gauge</key> + <value>False</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(774, 452)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>wxgui_scopesink2</key> + <param> + <key>id</key> + <value>wxgui_scopesink2_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>title</key> + <value>Scope Plot</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>v_scale</key> + <value>0</value> + </param> + <param> + <key>v_offset</key> + <value>0</value> + </param> + <param> + <key>t_scale</key> + <value>0</value> + </param> + <param> + <key>ac_couple</key> + <value>False</value> + </param> + <param> + <key>xy_mode</key> + <value>False</value> + </param> + <param> + <key>num_inputs</key> + <value>1</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>trig_mode</key> + <value>wxgui.TRIG_MODE_AUTO</value> + </param> + <param> + <key>y_axis_label</key> + <value>Counts</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(696, 244)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>wxgui_fftsink2</key> + <param> + <key>id</key> + <value>wxgui_fftsink2_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>title</key> + <value>FFT Plot</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>baseband_freq</key> + <value>0</value> + </param> + <param> + <key>y_per_div</key> + <value>10</value> + </param> + <param> + <key>y_divs</key> + <value>15</value> + </param> + <param> + <key>ref_level</key> + <value>0</value> + </param> + <param> + <key>ref_scale</key> + <value>2.0</value> + </param> + <param> + <key>fft_size</key> + <value>fft_size</value> + </param> + <param> + <key>fft_rate</key> + <value>20</value> + </param> + <param> + <key>peak_hold</key> + <value>False</value> + </param> + <param> + <key>average</key> + <value>False</value> + </param> + <param> + <key>avg_alpha</key> + <value>0</value> + </param> + <param> + <key>win</key> + <value>window.flattop</value> + </param> + <param> + <key>win_size</key> + <value></value> + </param> + <param> + <key>grid_pos</key> + <value></value> + </param> + <param> + <key>notebook</key> + <value></value> + </param> + <param> + <key>freqvar</key> + <value>None</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(894, 271)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_spec_an</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>wx_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>analog_noise_source_x_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_add_xx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>wxgui_fftsink2_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_noise_source_x_0</source_block_id> + <sink_block_id>blocks_rms_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_rms_xx_0</source_block_id> + <sink_block_id>wxgui_numbersink2_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_rms_xx_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_rms_xx_0_0</source_block_id> + <sink_block_id>wxgui_numbersink2_0_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_nlog10_ff_0</source_block_id> + <sink_block_id>snr_disp</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_divide_xx_0</source_block_id> + <sink_block_id>blocks_nlog10_ff_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_rms_xx_0</source_block_id> + <sink_block_id>blocks_divide_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_rms_xx_0_0</source_block_id> + <sink_block_id>blocks_divide_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>wxgui_scopesink2_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/examples/demo_two_tone.grc b/gr-channels/examples/demo_two_tone.grc new file mode 100644 index 0000000000..df544d8e85 --- /dev/null +++ b/gr-channels/examples/demo_two_tone.grc @@ -0,0 +1,1190 @@ +<?xml version='1.0' encoding='ASCII'?> +<flow_graph> + <timestamp>Thu Aug 1 13:37:58 2013</timestamp> + <block> + <key>options</key> + <param> + <key>id</key> + <value>demo_two_tone</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>title</key> + <value></value> + </param> + <param> + <key>author</key> + <value>mettus</value> + </param> + <param> + <key>description</key> + <value></value> + </param> + <param> + <key>window_size</key> + <value>1280, 1024</value> + </param> + <param> + <key>generate_options</key> + <value>qt_gui</value> + </param> + <param> + <key>category</key> + <value>Custom</value> + </param> + <param> + <key>run_options</key> + <value>prompt</value> + </param> + <param> + <key>run</key> + <value>True</value> + </param> + <param> + <key>max_nouts</key> + <value>0</value> + </param> + <param> + <key>realtime_scheduling</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(11, 93)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>const_size</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>value</key> + <value>4</value> + </param> + <param> + <key>_coordinate</key> + <value>(102, 93)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_throttle</key> + <param> + <key>id</key> + <value>blocks_throttle_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samples_per_second</key> + <value>samp_rate</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(397, 368)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_impairments</key> + <param> + <key>id</key> + <value>channels_impairments_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>phase_noise_mag</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>magbal</key> + <value>magbal</value> + </param> + <param> + <key>phasebal</key> + <value>0</value> + </param> + <param> + <key>q_ofs</key> + <value>q_ofs</value> + </param> + <param> + <key>i_ofs</key> + <value>i_ofs</value> + </param> + <param> + <key>freq_offset</key> + <value>0</value> + </param> + <param> + <key>gamma</key> + <value>gamma</value> + </param> + <param> + <key>beta</key> + <value>beta</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(578, 312)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>blocks_add_xx</key> + <param> + <key>id</key> + <value>blocks_add_xx_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>num_inputs</key> + <value>3</value> + </param> + <param> + <key>vlen</key> + <value>1</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(287, 338)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> + </param> + <param> + <key>freq</key> + <value>f2</value> + </param> + <param> + <key>amp</key> + <value>pow(10,sig_amp/20.0)*twotones</value> + </param> + <param> + <key>offset</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(24, 336)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_sig_source_x</key> + <param> + <key>id</key> + <value>analog_sig_source_x_1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>samp_rate</key> + <value>samp_rate</value> + </param> + <param> + <key>waveform</key> + <value>analog.GR_COS_WAVE</value> + </param> + <param> + <key>freq</key> + <value>f1</value> + </param> + <param> + <key>amp</key> + <value>pow(10,sig_amp/20.0)</value> + </param> + <param> + <key>offset</key> + <value>0</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(25, 225)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>analog_noise_source_x</key> + <param> + <key>id</key> + <value>analog_noise_source_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>noise_type</key> + <value>analog.GR_GAUSSIAN</value> + </param> + <param> + <key>amp</key> + <value>.00001</value> + </param> + <param> + <key>seed</key> + <value>42</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(24, 446)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>import</key> + <param> + <key>id</key> + <value>import_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>import</key> + <value>import math</value> + </param> + <param> + <key>_coordinate</key> + <value>(8, 160)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_phase_bal</key> + <param> + <key>id</key> + <value>channels_phase_bal_0</value> + </param> + <param> + <key>_enabled</key> + <value>False</value> + </param> + <param> + <key>alpha</key> + <value>0.00001</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(822, 329)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>channels_amp_bal</key> + <param> + <key>id</key> + <value>channels_amp_bal_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>alpha</key> + <value>0.0001</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>minoutbuf</key> + <value>0</value> + </param> + <param> + <key>_coordinate</key> + <value>(822, 392)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>sig_amp</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Singal Power (dB)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-100</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(214, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phase_noise_mag</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Phase Noise Mag (dB)</value> + </param> + <param> + <key>value</key> + <value>-100</value> + </param> + <param> + <key>start</key> + <value>-100</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>1,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(357, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>f1</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>f1</value> + </param> + <param> + <key>value</key> + <value>samp_rate/7.0</value> + </param> + <param> + <key>start</key> + <value>-samp_rate/2</value> + </param> + <param> + <key>stop</key> + <value>samp_rate/2</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(522, 8)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>f2</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>f2</value> + </param> + <param> + <key>value</key> + <value>samp_rate/9.0</value> + </param> + <param> + <key>start</key> + <value>-samp_rate/2</value> + </param> + <param> + <key>stop</key> + <value>samp_rate/2</value> + </param> + <param> + <key>step</key> + <value>1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>2,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(670, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>magbal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Magnitude Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>10</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(814, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>phasebal</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>IQ Phase Imbalance</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>0</value> + </param> + <param> + <key>stop</key> + <value>45</value> + </param> + <param> + <key>step</key> + <value>0.1</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>3,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(990, 9)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>gamma</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>k2 (IP2)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-.1</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>4,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(322, 138)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>beta</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>k3 (IP3)</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-.1</value> + </param> + <param> + <key>stop</key> + <value>0</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>4,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(215, 137)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_check_box</key> + <param> + <key>id</key> + <value>twotones</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Two Tones?</value> + </param> + <param> + <key>type</key> + <value>bool</value> + </param> + <param> + <key>value</key> + <value>False</value> + </param> + <param> + <key>true</key> + <value>True</value> + </param> + <param> + <key>false</key> + <value>False</value> + </param> + <param> + <key>gui_hint</key> + <value>0,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(650, 143)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>q_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>Q offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>5,1,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(538, 139)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable_qtgui_range</key> + <param> + <key>id</key> + <value>i_ofs</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>label</key> + <value>I offset</value> + </param> + <param> + <key>value</key> + <value>0</value> + </param> + <param> + <key>start</key> + <value>-1</value> + </param> + <param> + <key>stop</key> + <value>1</value> + </param> + <param> + <key>step</key> + <value>0.001</value> + </param> + <param> + <key>widget</key> + <value>counter_slider</value> + </param> + <param> + <key>orient</key> + <value>Qt.Horizontal</value> + </param> + <param> + <key>min_len</key> + <value>200</value> + </param> + <param> + <key>gui_hint</key> + <value>5,0,1,1</value> + </param> + <param> + <key>_coordinate</key> + <value>(430, 138)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>qtgui_freq_sink_x</key> + <param> + <key>id</key> + <value>qtgui_freq_sink_x_0</value> + </param> + <param> + <key>_enabled</key> + <value>True</value> + </param> + <param> + <key>type</key> + <value>complex</value> + </param> + <param> + <key>name</key> + <value>QT GUI Plot</value> + </param> + <param> + <key>fftsize</key> + <value>1024</value> + </param> + <param> + <key>wintype</key> + <value>firdes.WIN_BLACKMAN_hARRIS</value> + </param> + <param> + <key>fc</key> + <value>0</value> + </param> + <param> + <key>bw</key> + <value>samp_rate</value> + </param> + <param> + <key>ymin</key> + <value>-140</value> + </param> + <param> + <key>ymax</key> + <value>10</value> + </param> + <param> + <key>nconnections</key> + <value>1</value> + </param> + <param> + <key>update_time</key> + <value>0.10</value> + </param> + <param> + <key>gui_hint</key> + <value>6,0,1,2</value> + </param> + <param> + <key>affinity</key> + <value></value> + </param> + <param> + <key>_coordinate</key> + <value>(1059, 323)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>channels_phase_bal_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_impairments_0</source_block_id> + <sink_block_id>channels_amp_bal_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>blocks_throttle_0</source_block_id> + <sink_block_id>channels_impairments_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_noise_source_x_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>2</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_1</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>analog_sig_source_x_0</source_block_id> + <sink_block_id>blocks_add_xx_0</sink_block_id> + <source_key>0</source_key> + <sink_key>1</sink_key> + </connection> + <connection> + <source_block_id>blocks_add_xx_0</source_block_id> + <sink_block_id>blocks_throttle_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_phase_bal_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> + <connection> + <source_block_id>channels_amp_bal_0</source_block_id> + <sink_block_id>qtgui_freq_sink_x_0</sink_block_id> + <source_key>0</source_key> + <sink_key>0</sink_key> + </connection> +</flow_graph> diff --git a/gr-channels/grc/CMakeLists.txt b/gr-channels/grc/CMakeLists.txt index 3bda29f01d..75e2bc0ddd 100644 --- a/gr-channels/grc/CMakeLists.txt +++ b/gr-channels/grc/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2012 Free Software Foundation, Inc. +# Copyright 2012-2013 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -17,11 +17,7 @@ # the Free Software Foundation, Inc., 51 Franklin Street, # Boston, MA 02110-1301, USA. -install(FILES - channels_block_tree.xml - channels_channel_model.xml - channels_fading_model.xml - channels_selective_fading_model.xml - DESTINATION ${GRC_BLOCKS_DIR} - COMPONENT "channels_python" +file(GLOB xml_files "*.xml") +install(FILES ${xml_files} + DESTINATION ${GRC_BLOCKS_DIR} COMPONENT "channels_python" ) diff --git a/gr-channels/grc/channels_amp_bal.xml b/gr-channels/grc/channels_amp_bal.xml new file mode 100644 index 0000000000..7e0738ec98 --- /dev/null +++ b/gr-channels/grc/channels_amp_bal.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Amplitude balance +################################################### + --> +<block> + <name>Amplitude Balance</name> + <key>channels_amp_bal</key> + <import>from gnuradio import channels</import> + <make>channels.amp_bal($alpha)</make> + <callback>set_alpha($alpha)</callback> + <param> + <name>Alpha</name> + <key>alpha</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_block_tree.xml b/gr-channels/grc/channels_block_tree.xml index efa808fd53..154e9204d1 100644 --- a/gr-channels/grc/channels_block_tree.xml +++ b/gr-channels/grc/channels_block_tree.xml @@ -33,4 +33,15 @@ <block>channels_channel_model</block> <block>channels_fading_model</block> </cat> + <cat> + <name>Impairment Models</name> + <block>channels_impairments</block> + <block>channels_quantizer</block> + <block>channels_phase_noise_gen</block> + <block>channels_iqbal_gen</block> + <block>channels_distortion_2_gen</block> + <block>channels_distortion_3_gen</block> + <block>channels_amp_bal</block> + <block>channels_phase_bal</block> + </cat> </cat> diff --git a/gr-channels/grc/channels_channel_model2.xml b/gr-channels/grc/channels_channel_model2.xml new file mode 100644 index 0000000000..e8162f53d4 --- /dev/null +++ b/gr-channels/grc/channels_channel_model2.xml @@ -0,0 +1,61 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Channel Model +################################################### + --> +<block> + <name>Channel Model 2</name> + <key>channels_channel_model2</key> + <import>from gnuradio import channels</import> + <import>from gnuradio.filter import firdes</import> + <make>channels.channel_model2( + noise_voltage=$noise_voltage, + epsilon=$epsilon, + taps=$taps, + noise_seed=$seed, +)</make> + <callback>set_noise_voltage($noise_voltage)</callback> + <callback>set_taps($taps)</callback> + <callback>set_timing_offset($epsilon)</callback> + <param> + <name>Noise Voltage</name> + <key>noise_voltage</key> + <value>0.0</value> + <type>real</type> + </param> + <param> + <name>Time Offset</name> + <key>epsilon</key> + <value>1.0</value> + <type>real</type> + </param> + <param> + <name>Taps</name> + <key>taps</key> + <value>1.0 + 1.0j</value> + <type>complex_vector</type> + </param> + <param> + <name>Seed</name> + <key>seed</key> + <value>0</value> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <sink> + <name>freq</name> + <type>float</type> + </sink> + <sink> + <name>time</name> + <type>float</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_conj_fs_iqcorr.xml b/gr-channels/grc/channels_conj_fs_iqcorr.xml new file mode 100644 index 0000000000..354f1a2030 --- /dev/null +++ b/gr-channels/grc/channels_conj_fs_iqcorr.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Freq. Selective IQ Correction +################################################### + --> +<block> + <name>Freq. Selective IQ Correction</name> + <key>channels_conj_fs_iqcorr</key> + <import>from gnuradio import channels</import> + <make>channels.conj_fs_iqcorr($delay, $taps)</make> + <callback>set_delay($delay)</callback> + <callback>set_taps($taps)</callback> + <param> + <name>Delay</name> + <key>delay</key> + <value>0</value> + <type>int</type> + </param> + <param> + <name>Taps</name> + <key>taps</key> + <value>0</value> + <type>complex_vector</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_distortion_2_gen.xml b/gr-channels/grc/channels_distortion_2_gen.xml new file mode 100644 index 0000000000..5b117a9d00 --- /dev/null +++ b/gr-channels/grc/channels_distortion_2_gen.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Second Order Distortion Generator +################################################### + --> +<block> + <name>Second Order Distortion</name> + <key>channels_distortion_2_gen</key> + <import>from gnuradio import channels</import> + <make>channels.distortion_2_gen($beta)</make> + <callback>set_beta($beta)</callback> + <param> + <name>Distortion</name> + <key>beta</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_distortion_3_gen.xml b/gr-channels/grc/channels_distortion_3_gen.xml new file mode 100644 index 0000000000..7bf145c27e --- /dev/null +++ b/gr-channels/grc/channels_distortion_3_gen.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Third Order Distortion Generator +################################################### + --> +<block> + <name>Third Order Distortion</name> + <key>channels_distortion_3_gen</key> + <import>from gnuradio import channels</import> + <make>channels.distortion_3_gen($beta)</make> + <callback>set_beta($beta)</callback> + <param> + <name>Distortion</name> + <key>beta</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_impairments.xml b/gr-channels/grc/channels_impairments.xml new file mode 100644 index 0000000000..12ee65c740 --- /dev/null +++ b/gr-channels/grc/channels_impairments.xml @@ -0,0 +1,76 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Impairments Model +################################################### + --> +<block> + <name>HW Impairments</name> + <key>channels_impairments</key> + <import>from gnuradio import channels</import> + <make>channels.impairments($phase_noise_mag, $magbal, $phasebal, $q_ofs, $i_ofs, $freq_offset, $gamma, $beta)</make> + <callback>set_phase_noise_mag($phase_noise_mag)</callback> + <callback>set_magbal($magbal)</callback> + <callback>set_phasebal($phasebal)</callback> + <callback>set_q_ofs($q_ofs)</callback> + <callback>set_i_ofs($i_ofs)</callback> + <callback>set_freq_offset($freq_offset)</callback> + <callback>set_gamma($gamma)</callback> + <callback>set_beta($beta)</callback> + <param> + <name>Phase Noise Mag.</name> + <key>phase_noise_mag</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>IQ Mag. Imbalance</name> + <key>magbal</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>IQ Phase. Imbalance</name> + <key>phasebal</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Quadrature Offset</name> + <key>q_ofs</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Inphase Offset</name> + <key>i_ofs</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Frequency Offset</name> + <key>freq_offset</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Second Order Distortion</name> + <key>gamma</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Third Order Distortion</name> + <key>beta</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_iqbal_gen.xml b/gr-channels/grc/channels_iqbal_gen.xml new file mode 100644 index 0000000000..9cb267adec --- /dev/null +++ b/gr-channels/grc/channels_iqbal_gen.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<!-- +################################################### +##IQ Imbalance Generator +################################################### + --> +<block> + <name>IQ Imbalance Generator</name> + <key>channels_iqbal_gen</key> + <import>from gnuradio import channels</import> + <make>channels.iqbal_gen($mag, $phase)</make> + <callback>set_magnitude($mag)</callback> + <callback>set_phase($phase)</callback> + <param> + <name>Magnitude</name> + <key>mag</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Phase</name> + <key>phase</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_phase_bal.xml b/gr-channels/grc/channels_phase_bal.xml new file mode 100644 index 0000000000..ce17ab79ab --- /dev/null +++ b/gr-channels/grc/channels_phase_bal.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Phase balance +################################################### + --> +<block> + <name>Phase Balance</name> + <key>channels_phase_bal</key> + <import>from gnuradio import channels</import> + <make>channels.phase_bal($alpha)</make> + <callback>set_alpha($alpha)</callback> + <param> + <name>Alpha</name> + <key>alpha</key> + <value>0</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_phase_noise_gen.xml b/gr-channels/grc/channels_phase_noise_gen.xml new file mode 100644 index 0000000000..852b4c9966 --- /dev/null +++ b/gr-channels/grc/channels_phase_noise_gen.xml @@ -0,0 +1,34 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Phase Noise Generator +################################################### + --> +<block> + <name>Phase Noise Generator</name> + <key>channels_phase_noise_gen</key> + <import>from gnuradio import channels</import> + <make>channels.phase_noise_gen($noise_mag, $alpha)</make> + <callback>set_noise_mag($noise_mag)</callback> + <callback>set_alpha($alpha)</callback> + <param> + <name>Noise Magnitude</name> + <key>noise_mag</key> + <value>0</value> + <type>float</type> + </param> + <param> + <name>Alpha</name> + <key>alpha</key> + <value>0.1</value> + <type>float</type> + </param> + <sink> + <name>in</name> + <type>complex</type> + </sink> + <source> + <name>out</name> + <type>complex</type> + </source> +</block> diff --git a/gr-channels/grc/channels_quantizer.xml b/gr-channels/grc/channels_quantizer.xml new file mode 100644 index 0000000000..57bed74ee7 --- /dev/null +++ b/gr-channels/grc/channels_quantizer.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Quantization +################################################### + --> +<block> + <name>Quantizer</name> + <key>channels_quantizer</key> + <import>from gnuradio import channels</import> + <make>channels.quantizer($bits)</make> + <callback>set_bits($bits)</callback> + <param> + <name>Bits</name> + <key>bits</key> + <value>16</value> + <type>int</type> + </param> + <sink> + <name>in</name> + <type>float</type> + </sink> + <source> + <name>out</name> + <type>float</type> + </source> +</block> diff --git a/gr-channels/include/gnuradio/channels/CMakeLists.txt b/gr-channels/include/gnuradio/channels/CMakeLists.txt index f9a535b9c0..cb618a7e8d 100644 --- a/gr-channels/include/gnuradio/channels/CMakeLists.txt +++ b/gr-channels/include/gnuradio/channels/CMakeLists.txt @@ -23,6 +23,7 @@ install(FILES api.h channel_model.h + channel_model2.h fading_model.h selective_fading_model.h DESTINATION ${GR_INCLUDE_DIR}/gnuradio/channels diff --git a/gr-channels/include/gnuradio/channels/channel_model2.h b/gr-channels/include/gnuradio/channels/channel_model2.h new file mode 100644 index 0000000000..05084931ee --- /dev/null +++ b/gr-channels/include/gnuradio/channels/channel_model2.h @@ -0,0 +1,102 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CHANNELS_CHANNEL_MODEL2_H +#define INCLUDED_CHANNELS_CHANNEL_MODEL2_H + +#include <gnuradio/channels/api.h> +#include <gnuradio/hier_block2.h> +#include <gnuradio/types.h> + +namespace gr { + namespace channels { + + /*! + * \brief channel model2 + * \ingroup channel_models_blk + * + * \details + * This block implements a basic channel model simulator that can + * be used to help evaluate, design, and test various signals, + * waveforms, and algorithms. + * + * This model allows the user to set the voltage of an AWGN noise + * source, an initial timing offset, and a noise seed to randomize + * the AWGN noise source. + * + * Multipath can be approximated in this model by using a FIR + * filter representation of a multipath delay profile. + * + * Unlike gr::channels::channel_model, this block is designed to + * enable time-varying frequency and timing offsets. + * * Port 0: input signal to be run through the channel. + * * Port 1: frequency function. A constant value between -0.5 and + * 0.5 here will turn into a constant frequency offset + * from -fs/2 to fs/2 (where fs is the sample rate). + * * Port 2: timing offset function. Sets the resampling rate of + * the channel model. A constant value here produces + * that value as the timing offset, so a constant 1.0 + * input stream is the same as not having a timing + * offset. + * + * Since the models for frequency and timing offset may vary and + * what we are trying to model may be different for different + * simulations, we provide the time-varying nature as an input + * function that is user-defined. If only constant frequency and + * timing offsets are required, it is easier and less expensive to + * use gr::channels::channel_model. + */ + class CHANNELS_API channel_model2 : virtual public hier_block2 + { + public: + // gr::channels::channel_model2::sptr + typedef boost::shared_ptr<channel_model2> sptr; + + /*! \brief Build the channel simulator. + * + * \param noise_voltage The AWGN noise level as a voltage (to be + * calculated externally to meet, say, a + * desired SNR). + * \param epsilon The initial sample timing offset to emulate the + * different rates between the sample clocks of + * the transmitter and receiver. 1.0 is no difference. + * \param taps Taps of a FIR filter to emulate a multipath delay profile. + * \param noise_seed A random number generator seed for the noise source. + */ + static sptr make(double noise_voltage=0.0, + double epsilon=1.0, + const std::vector<gr_complex> &taps=std::vector<gr_complex>(1,1), + double noise_seed=0); + + virtual void set_noise_voltage(double noise_voltage) = 0; + virtual void set_taps(const std::vector<gr_complex> &taps) = 0; + virtual void set_timing_offset(double epsilon) = 0; + + virtual double noise_voltage() const = 0; + virtual std::vector<gr_complex> taps() const = 0; + virtual double timing_offset() const = 0; + }; + + } /* namespace channels */ +} /* namespace gr */ + +#endif /* INCLUDED_CHANNELS_CHANNEL_MODEL2_H */ diff --git a/gr-channels/include/gnuradio/channels/fading_model.h b/gr-channels/include/gnuradio/channels/fading_model.h index c5946d3598..fe8bb33615 100644 --- a/gr-channels/include/gnuradio/channels/fading_model.h +++ b/gr-channels/include/gnuradio/channels/fading_model.h @@ -47,11 +47,11 @@ namespace gr { /*! \brief Build the channel simulator. * - * \param N The number of sinusiods to use in simulating the channel 8 is a good value - * \param fDTs normalized maximum doppler frequency, fD * Ts - * \param LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models - * \param K Rician factor (ratio of the specular power to the scattered power) - * \param seed a random number to seed the noise generators + * \param N The number of sinusiods to use in simulating the channel; 8 is a good value + * \param fDTs normalized maximum Doppler frequency, fD * Ts + * \param LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models + * \param K Rician factor (ratio of the specular power to the scattered power) + * \param seed a random number to seed the noise generators */ static sptr make(unsigned int N, float fDTs=0.01, diff --git a/gr-channels/include/gnuradio/channels/selective_fading_model.h b/gr-channels/include/gnuradio/channels/selective_fading_model.h index 3135f5caae..914388c5fe 100644 --- a/gr-channels/include/gnuradio/channels/selective_fading_model.h +++ b/gr-channels/include/gnuradio/channels/selective_fading_model.h @@ -50,14 +50,14 @@ namespace gr { /*! \brief Build the channel simulator. * - * \param N The number of sinusiods to use in simulating the channel 8 is a good value - * \param fDTs normalized maximum doppler frequency, fD * Ts - * \param LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models - * \param K Rician factor (ratio of the specular power to the scattered power) - * \param seed a random number to seed the noise generators - * \param delays A vector of values the specify the time delay of each impulse - * \param mags A vector of values that specifies the magnitude of each inpulse - * \param ntaps The number of filter taps. + * \param N The number of sinusiods to use in simulating the channel; 8 is a good value + * \param fDTs normalized maximum Doppler frequency, fD * Ts + * \param LOS include Line-of-Site path? selects between Rayleigh (NLOS) and Rician (LOS) models + * \param K Rician factor (ratio of the specular power to the scattered power) + * \param seed a random number to seed the noise generators + * \param delays A vector of values the specify the time delay of each impulse + * \param mags A vector of values that specifies the magnitude of each impulse + * \param ntaps The number of filter taps. */ static sptr make(unsigned int N, float fDTs, diff --git a/gr-channels/lib/CMakeLists.txt b/gr-channels/lib/CMakeLists.txt index 46c90671c2..f429fae0fc 100644 --- a/gr-channels/lib/CMakeLists.txt +++ b/gr-channels/lib/CMakeLists.txt @@ -43,6 +43,7 @@ endif(ENABLE_GR_CTRLPORT) ######################################################################## list(APPEND channels_sources channel_model_impl.cc + channel_model2_impl.cc fading_model_impl.cc selective_fading_model_impl.cc flat_fader_impl.cc diff --git a/gr-channels/lib/channel_model2_impl.cc b/gr-channels/lib/channel_model2_impl.cc new file mode 100644 index 0000000000..13eb033def --- /dev/null +++ b/gr-channels/lib/channel_model2_impl.cc @@ -0,0 +1,176 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + + #define _USE_MATH_DEFINES + +#include "channel_model2_impl.h" +#include <gnuradio/io_signature.h> +#include <iostream> + +namespace gr { + namespace channels { + + channel_model2::sptr + channel_model2::make(double noise_voltage, + double epsilon, + const std::vector<gr_complex> &taps, + double noise_seed) + { + return gnuradio::get_initial_sptr + (new channel_model2_impl(noise_voltage, + epsilon, + taps, + noise_seed)); + } + + // Hierarchical block constructor + channel_model2_impl::channel_model2_impl(double noise_voltage, + double epsilon, + const std::vector<gr_complex> &taps, + double noise_seed) + : hier_block2("channel_model2", + io_signature::make2(3, 3, sizeof(gr_complex), sizeof(float)), + io_signature::make(1, 1, sizeof(gr_complex))) + { + d_taps = taps; + while(d_taps.size() < 2) { + d_taps.push_back(0); + } + + d_timing_offset = filter::fractional_resampler_cc::make(0, epsilon); + + d_multipath = filter::fir_filter_ccc::make(1, d_taps); + + d_noise_adder = blocks::add_cc::make(); + d_noise = analog::fastnoise_source_c::make(analog::GR_GAUSSIAN, + noise_voltage, noise_seed); + d_freq_gen = blocks::vco_c::make(1.0, 2*M_PI, 1.0); + + d_mixer_offset = blocks::multiply_cc::make(); + + connect(self(), 0, d_timing_offset, 0); + connect(self(), 2, d_timing_offset, 1); + connect(d_timing_offset, 0, d_multipath, 0); + connect(d_multipath, 0, d_mixer_offset, 0); + + connect(self(), 1, d_freq_gen, 0); + connect(d_freq_gen, 0, d_mixer_offset, 1); + + connect(d_mixer_offset, 0, d_noise_adder, 1); + connect(d_noise, 0, d_noise_adder, 0); + connect(d_noise_adder, 0, self(), 0); + } + + channel_model2_impl::~channel_model2_impl() + { + } + + void + channel_model2_impl::set_noise_voltage(double noise_voltage) + { + d_noise->set_amplitude(noise_voltage); + } + + void + channel_model2_impl::set_taps(const std::vector<gr_complex> &taps) + { + d_taps = taps; + while(d_taps.size() < 2) { + d_taps.push_back(0); + } + d_multipath->set_taps(d_taps); + } + + void + channel_model2_impl::set_timing_offset(double epsilon) + { + d_timing_offset->set_resamp_ratio(epsilon); + } + + double + channel_model2_impl::noise_voltage() const + { + return d_noise->amplitude(); + } + + std::vector<gr_complex> + channel_model2_impl::taps() const + { + return d_multipath->taps(); + } + + double + channel_model2_impl::timing_offset() const + { + return d_timing_offset->resamp_ratio(); + } + + void + channel_model2_impl::setup_rpc() + { +#ifdef GR_CTRLPORT + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<channel_model2, double>( + alias(), "noise", + &channel_model2::noise_voltage, + pmt::mp(-10.0f), pmt::mp(10.0f), pmt::mp(0.0f), + "", "Noise Voltage", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<channel_model2, double>( + alias(), "timing", + &channel_model2::timing_offset, + pmt::mp(0.0), pmt::mp(2.0), pmt::mp(0.0), + "", "Timing Offset", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_get<channel_model2, std::vector<gr_complex> >( + alias(), "taps", + &channel_model2::taps, + pmt::make_c32vector(0,-10), + pmt::make_c32vector(0,10), + pmt::make_c32vector(0,0), + "", "Multipath taps", RPC_PRIVLVL_MIN, + DISPTIME | DISPOPTCPLX | DISPOPTSTRIP))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<channel_model2, double>( + alias(), "noise", + &channel_model2::set_noise_voltage, + pmt::mp(-10.0), pmt::mp(10.0), pmt::mp(0.0), + "V", "Noise Voltage", + RPC_PRIVLVL_MIN, DISPNULL))); + + add_rpc_variable( + rpcbasic_sptr(new rpcbasic_register_set<channel_model2, double>( + alias(), "timing", + &channel_model2::set_timing_offset, + pmt::mp(0.0f), pmt::mp(2.0f), pmt::mp(0.0f), + "", "Timing Offset", + RPC_PRIVLVL_MIN, DISPNULL))); +#endif /* GR_CTRLPORT */ + } + + } /* namespace channels */ +} /* namespace gr */ diff --git a/gr-channels/lib/channel_model2_impl.h b/gr-channels/lib/channel_model2_impl.h new file mode 100644 index 0000000000..db2a667f9c --- /dev/null +++ b/gr-channels/lib/channel_model2_impl.h @@ -0,0 +1,76 @@ +/* -*- c++ -*- */ +/* + * Copyright 2009,2012,2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_CHANNELS_CHANNEL_MODEL2_IMPL_H +#define INCLUDED_CHANNELS_CHANNEL_MODEL2_IMPL_H + +#include <gnuradio/channels/channel_model2.h> +#include <gnuradio/top_block.h> +#include <gnuradio/blocks/add_cc.h> +#include <gnuradio/blocks/multiply_cc.h> +#include <gnuradio/analog/sig_source_c.h> +#include <gnuradio/analog/fastnoise_source_c.h> +#include <gnuradio/filter/fractional_resampler_cc.h> +#include <gnuradio/filter/fir_filter_ccc.h> +#include <gnuradio/blocks/vco_c.h> + +namespace gr { + namespace channels { + + class CHANNELS_API channel_model2_impl : public channel_model2 + { + private: + blocks::add_cc::sptr d_noise_adder; + blocks::multiply_cc::sptr d_mixer_offset; + + blocks::vco_c::sptr d_freq_gen; + + analog::fastnoise_source_c::sptr d_noise; + + filter::fractional_resampler_cc::sptr d_timing_offset; + filter::fir_filter_ccc::sptr d_multipath; + + std::vector<gr_complex> d_taps; + + public: + channel_model2_impl(double noise_voltage, + double epsilon, + const std::vector<gr_complex> &taps, + double noise_seed); + + ~channel_model2_impl(); + + void setup_rpc(); + + void set_noise_voltage(double noise_voltage); + void set_taps(const std::vector<gr_complex> &taps); + void set_timing_offset(double epsilon); + + double noise_voltage() const; + std::vector<gr_complex> taps() const; + double timing_offset() const; + }; + + } /* namespace channels */ +} /* namespace gr */ + +#endif /* INCLUDED_CHANNELS_CHANNEL_MODEL2_IMPL_H */ diff --git a/gr-channels/python/channels/CMakeLists.txt b/gr-channels/python/channels/CMakeLists.txt index 4ebf029b75..1f6f08bb69 100644 --- a/gr-channels/python/channels/CMakeLists.txt +++ b/gr-channels/python/channels/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2012 Free Software Foundation, Inc. +# Copyright 2012-2013 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -23,6 +23,15 @@ include(GrPython) GR_PYTHON_INSTALL( FILES __init__.py + amp_bal.py + conj_fs_iqcorr.py + distortion_2_gen.py + distortion_3_gen.py + impairments.py + iqbal_gen.py + phase_bal.py + phase_noise_gen.py + quantizer.py DESTINATION ${GR_PYTHON_DIR}/gnuradio/channels COMPONENT "channels_python" ) diff --git a/gr-channels/python/channels/__init__.py b/gr-channels/python/channels/__init__.py index c4d3271f28..ae4c4ab715 100644 --- a/gr-channels/python/channels/__init__.py +++ b/gr-channels/python/channels/__init__.py @@ -1,5 +1,5 @@ # -# Copyright 2012 Free Software Foundation, Inc. +# Copyright 2012-2013 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -31,3 +31,14 @@ except ImportError: dirname, filename = os.path.split(os.path.abspath(__file__)) __path__.append(os.path.join(dirname, "..", "..", "swig")) from channels_swig import * + +# Blocks for Hardware Impairments +from amp_bal import * +from conj_fs_iqcorr import * +from distortion_2_gen import * +from distortion_3_gen import * +from iqbal_gen import * +from impairments import * +from phase_bal import * +from phase_noise_gen import * +from quantizer import * diff --git a/gr-channels/python/channels/amp_bal.py b/gr-channels/python/channels/amp_bal.py new file mode 100644 index 0000000000..30e0f0d8e2 --- /dev/null +++ b/gr-channels/python/channels/amp_bal.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Amplitude Balance +# Author: mettus +# Description: Restores IQ amplitude balance +# Generated: Thu Aug 1 11:47:46 2013 +################################################## + +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes + +class amp_bal(gr.hier_block2): + + def __init__(self, alpha=0): + gr.hier_block2.__init__( + self, "Amplitude Balance", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.alpha = alpha + + ################################################## + # Blocks + ################################################## + self.blocks_rms_xx0 = blocks.rms_ff(alpha) + self.blocks_rms_xx = blocks.rms_ff(alpha) + self.blocks_multiply_vxx1 = blocks.multiply_vff(1) + self.blocks_float_to_complex = blocks.float_to_complex(1) + self.blocks_divide_xx = blocks.divide_ff(1) + self.blocks_complex_to_float = blocks.complex_to_float(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_float_to_complex, 0), (self, 0)) + self.connect((self, 0), (self.blocks_complex_to_float, 0)) + self.connect((self.blocks_complex_to_float, 0), (self.blocks_rms_xx, 0)) + self.connect((self.blocks_complex_to_float, 1), (self.blocks_rms_xx0, 0)) + self.connect((self.blocks_rms_xx, 0), (self.blocks_divide_xx, 0)) + self.connect((self.blocks_rms_xx0, 0), (self.blocks_divide_xx, 1)) + self.connect((self.blocks_complex_to_float, 0), (self.blocks_float_to_complex, 0)) + self.connect((self.blocks_complex_to_float, 1), (self.blocks_multiply_vxx1, 1)) + self.connect((self.blocks_divide_xx, 0), (self.blocks_multiply_vxx1, 0)) + self.connect((self.blocks_multiply_vxx1, 0), (self.blocks_float_to_complex, 1)) + + +# QT sink close method reimplementation + + def get_alpha(self): + return self.alpha + + def set_alpha(self, alpha): + self.alpha = alpha + self.blocks_rms_xx.set_alpha(self.alpha) + self.blocks_rms_xx0.set_alpha(self.alpha) + + diff --git a/gr-channels/python/channels/conj_fs_iqcorr.py b/gr-channels/python/channels/conj_fs_iqcorr.py new file mode 100644 index 0000000000..700eb645c2 --- /dev/null +++ b/gr-channels/python/channels/conj_fs_iqcorr.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Conj FS IQBal +# Author: matt@ettus.com +# Description: Frequency Selective Conjugate Method IQ Balance Corrector +# Generated: Thu Aug 1 13:00:27 2013 +################################################## + +from gnuradio import blocks +from gnuradio import filter +from gnuradio import gr +from gnuradio.filter import firdes + +class conj_fs_iqcorr(gr.hier_block2): + + def __init__(self, delay=0, taps=[]): + gr.hier_block2.__init__( + self, "Conj FS IQBal", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.delay = delay + self.taps = taps + + ################################################## + # Blocks + ################################################## + self.filter_fir_filter_xxx_0 = filter.fir_filter_ccc(1, (taps)) + self.delay_0 = blocks.delay(gr.sizeof_gr_complex*1, delay) + self.blocks_conjugate_cc_0 = blocks.conjugate_cc() + self.blocks_add_xx_0 = blocks.add_vcc(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_add_xx_0, 0), (self, 0)) + self.connect((self, 0), (self.blocks_conjugate_cc_0, 0)) + self.connect((self.filter_fir_filter_xxx_0, 0), (self.blocks_add_xx_0, 1)) + self.connect((self.blocks_conjugate_cc_0, 0), (self.filter_fir_filter_xxx_0, 0)) + self.connect((self, 0), (self.delay_0, 0)) + self.connect((self.delay_0, 0), (self.blocks_add_xx_0, 0)) + + +# QT sink close method reimplementation + + def get_delay(self): + return self.delay + + def set_delay(self, delay): + self.delay = delay + self.delay_0.set_dly(self.delay) + + def get_taps(self): + return self.taps + + def set_taps(self, taps): + self.taps = taps + self.filter_fir_filter_xxx_0.set_taps((self.taps)) + + diff --git a/gr-channels/python/channels/distortion_2_gen.py b/gr-channels/python/channels/distortion_2_gen.py new file mode 100644 index 0000000000..f8933cf7aa --- /dev/null +++ b/gr-channels/python/channels/distortion_2_gen.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Second Order Distortion +# Author: mettus +# Generated: Thu Aug 1 12:30:23 2013 +################################################## + +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes +import math + +class distortion_2_gen(gr.hier_block2): + + def __init__(self, beta=0): + gr.hier_block2.__init__( + self, "Second Order Distortion", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.beta = beta + + ################################################## + # Blocks + ################################################## + self.blocks_multiply_xx_0_0 = blocks.multiply_vcc(1) + self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vcc((beta, )) + self.blocks_conjugate_cc_0 = blocks.conjugate_cc() + self.blocks_add_xx_0_0 = blocks.add_vcc(1) + self.blocks_add_xx_0 = blocks.add_vcc(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_add_xx_0, 1)) + self.connect((self, 0), (self.blocks_multiply_xx_0, 0)) + self.connect((self, 0), (self.blocks_add_xx_0, 0)) + self.connect((self.blocks_add_xx_0, 0), (self, 0)) + self.connect((self, 0), (self.blocks_conjugate_cc_0, 0)) + self.connect((self.blocks_conjugate_cc_0, 0), (self.blocks_multiply_xx_0_0, 1)) + self.connect((self, 0), (self.blocks_multiply_xx_0, 1)) + self.connect((self, 0), (self.blocks_multiply_xx_0_0, 0)) + self.connect((self.blocks_multiply_xx_0_0, 0), (self.blocks_add_xx_0_0, 1)) + self.connect((self.blocks_multiply_xx_0, 0), (self.blocks_add_xx_0_0, 0)) + self.connect((self.blocks_add_xx_0_0, 0), (self.blocks_multiply_const_vxx_0, 0)) + + +# QT sink close method reimplementation + + def get_beta(self): + return self.beta + + def set_beta(self, beta): + self.beta = beta + self.blocks_multiply_const_vxx_0.set_k((self.beta, )) + + diff --git a/gr-channels/python/channels/distortion_3_gen.py b/gr-channels/python/channels/distortion_3_gen.py new file mode 100644 index 0000000000..1607e01bf2 --- /dev/null +++ b/gr-channels/python/channels/distortion_3_gen.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Third Order Distortion +# Author: mettus +# Generated: Thu Aug 1 12:37:59 2013 +################################################## + +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes +import math + +class distortion_3_gen(gr.hier_block2): + + def __init__(self, beta=0): + gr.hier_block2.__init__( + self, "Third Order Distortion", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.beta = beta + + ################################################## + # Blocks + ################################################## + self.blocks_null_source_0 = blocks.null_source(gr.sizeof_float*1) + self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vcc((beta, )) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(1) + self.blocks_add_xx_0 = blocks.add_vcc(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_float_to_complex_0, 0), (self.blocks_multiply_xx_0, 1)) + self.connect((self.blocks_null_source_0, 0), (self.blocks_float_to_complex_0, 1)) + self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_add_xx_0, 1)) + self.connect((self.blocks_multiply_xx_0, 0), (self.blocks_multiply_const_vxx_0, 0)) + self.connect((self, 0), (self.blocks_complex_to_mag_squared_0, 0)) + self.connect((self, 0), (self.blocks_multiply_xx_0, 0)) + self.connect((self, 0), (self.blocks_add_xx_0, 0)) + self.connect((self.blocks_add_xx_0, 0), (self, 0)) + + +# QT sink close method reimplementation + + def get_beta(self): + return self.beta + + def set_beta(self, beta): + self.beta = beta + self.blocks_multiply_const_vxx_0.set_k((self.beta, )) + + diff --git a/gr-channels/python/channels/impairments.py b/gr-channels/python/channels/impairments.py new file mode 100644 index 0000000000..3da838a902 --- /dev/null +++ b/gr-channels/python/channels/impairments.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Radio Impairments Model +# Author: mettus +# Generated: Thu Aug 1 12:46:10 2013 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes +import math + +#Import locally +from phase_noise_gen import * +from iqbal_gen import * +from distortion_2_gen import * +from distortion_3_gen import * + +class impairments(gr.hier_block2): + + def __init__(self, phase_noise_mag=0, magbal=0, phasebal=0, q_ofs=0, i_ofs=0, freq_offset=0, gamma=0, beta=0): + gr.hier_block2.__init__( + self, "Radio Impairments Model", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.phase_noise_mag = phase_noise_mag + self.magbal = magbal + self.phasebal = phasebal + self.q_ofs = q_ofs + self.i_ofs = i_ofs + self.freq_offset = freq_offset + self.gamma = gamma + self.beta = beta + + ################################################## + # Blocks + ################################################## + self.channels_phase_noise_gen_0_0 = phase_noise_gen(math.pow(10.0,phase_noise_mag/20.0), .01) + self.channels_iqbal_gen_0 = iqbal_gen(magbal, phasebal) + self.channels_distortion_3_gen_0 = distortion_3_gen(beta) + self.channels_distortion_2_gen_0 = distortion_2_gen(gamma) + self.blocks_multiply_xx_0_0 = blocks.multiply_vcc(1) + self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) + self.blocks_conjugate_cc_0 = blocks.conjugate_cc() + self.blocks_add_const_vxx_0 = blocks.add_const_vcc((i_ofs + q_ofs* 1j, )) + self.analog_sig_source_x_0 = analog.sig_source_c(1.0, analog.GR_COS_WAVE, freq_offset, 1, 0) + + ################################################## + # Connections + ################################################## + self.connect((self.channels_phase_noise_gen_0_0, 0), (self.channels_distortion_3_gen_0, 0)) + self.connect((self.blocks_multiply_xx_0, 0), (self, 0)) + self.connect((self.blocks_add_const_vxx_0, 0), (self.blocks_multiply_xx_0, 1)) + self.connect((self.analog_sig_source_x_0, 0), (self.blocks_multiply_xx_0, 0)) + self.connect((self.blocks_multiply_xx_0_0, 0), (self.channels_phase_noise_gen_0_0, 0)) + self.connect((self.analog_sig_source_x_0, 0), (self.blocks_conjugate_cc_0, 0)) + self.connect((self, 0), (self.blocks_multiply_xx_0_0, 1)) + self.connect((self.blocks_conjugate_cc_0, 0), (self.blocks_multiply_xx_0_0, 0)) + self.connect((self.channels_iqbal_gen_0, 0), (self.blocks_add_const_vxx_0, 0)) + self.connect((self.channels_distortion_3_gen_0, 0), (self.channels_distortion_2_gen_0, 0)) + self.connect((self.channels_distortion_2_gen_0, 0), (self.channels_iqbal_gen_0, 0)) + + +# QT sink close method reimplementation + + def get_phase_noise_mag(self): + return self.phase_noise_mag + + def set_phase_noise_mag(self, phase_noise_mag): + self.phase_noise_mag = phase_noise_mag + self.channels_phase_noise_gen_0_0.set_noise_mag(math.pow(10.0,self.phase_noise_mag/20.0)) + + def get_magbal(self): + return self.magbal + + def set_magbal(self, magbal): + self.magbal = magbal + self.channels_iqbal_gen_0.set_magnitude(self.magbal) + + def get_phasebal(self): + return self.phasebal + + def set_phasebal(self, phasebal): + self.phasebal = phasebal + self.channels_iqbal_gen_0.set_phase(self.phasebal) + + def get_q_ofs(self): + return self.q_ofs + + def set_q_ofs(self, q_ofs): + self.q_ofs = q_ofs + self.blocks_add_const_vxx_0.set_k((self.i_ofs + self.q_ofs* 1j, )) + + def get_i_ofs(self): + return self.i_ofs + + def set_i_ofs(self, i_ofs): + self.i_ofs = i_ofs + self.blocks_add_const_vxx_0.set_k((self.i_ofs + self.q_ofs* 1j, )) + + def get_freq_offset(self): + return self.freq_offset + + def set_freq_offset(self, freq_offset): + self.freq_offset = freq_offset + self.analog_sig_source_x_0.set_frequency(self.freq_offset) + + def get_gamma(self): + return self.gamma + + def set_gamma(self, gamma): + self.gamma = gamma + self.channels_distortion_2_gen_0.set_beta(self.gamma) + + def get_beta(self): + return self.beta + + def set_beta(self, beta): + self.beta = beta + self.channels_distortion_3_gen_0.set_beta(self.beta) + + diff --git a/gr-channels/python/channels/iqbal_gen.py b/gr-channels/python/channels/iqbal_gen.py new file mode 100644 index 0000000000..d42ca22778 --- /dev/null +++ b/gr-channels/python/channels/iqbal_gen.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: IQ Imbalance Generator +# Author: mettus +# Generated: Thu Aug 1 12:08:07 2013 +################################################## + +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes +import math + +class iqbal_gen(gr.hier_block2): + + def __init__(self, magnitude=0, phase=0): + gr.hier_block2.__init__( + self, "IQ Imbalance Generator", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.magnitude = magnitude + self.phase = phase + + ################################################## + # Blocks + ################################################## + self.mag = blocks.multiply_const_vff((math.pow(10,magnitude/20.0), )) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((math.sin(phase*math.pi/180.0), )) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.blocks_complex_to_float_0 = blocks.complex_to_float(1) + self.blocks_add_xx_0 = blocks.add_vff(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_float_to_complex_0, 0), (self, 0)) + self.connect((self, 0), (self.blocks_complex_to_float_0, 0)) + self.connect((self.blocks_complex_to_float_0, 0), (self.mag, 0)) + self.connect((self.mag, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_add_xx_0, 0), (self.blocks_float_to_complex_0, 1)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_add_xx_0, 0)) + self.connect((self.blocks_complex_to_float_0, 1), (self.blocks_add_xx_0, 1)) + self.connect((self.blocks_complex_to_float_0, 0), (self.blocks_multiply_const_vxx_0, 0)) + + +# QT sink close method reimplementation + + def get_magnitude(self): + return self.magnitude + + def set_magnitude(self, magnitude): + self.magnitude = magnitude + self.mag.set_k((math.pow(10,self.magnitude/20.0), )) + + def get_phase(self): + return self.phase + + def set_phase(self, phase): + self.phase = phase + self.blocks_multiply_const_vxx_0.set_k((math.sin(self.phase*math.pi/180.0), )) + + diff --git a/gr-channels/python/channels/phase_bal.py b/gr-channels/python/channels/phase_bal.py new file mode 100644 index 0000000000..b760e6f439 --- /dev/null +++ b/gr-channels/python/channels/phase_bal.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: IQ Phase Balancer +# Author: matt@ettus.com +# Generated: Thu Aug 1 11:49:41 2013 +################################################## + +from gnuradio import blocks +from gnuradio import filter +from gnuradio import gr +from gnuradio.filter import firdes + +class phase_bal(gr.hier_block2): + + def __init__(self, alpha=0): + gr.hier_block2.__init__( + self, "IQ Phase Balancer", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.alpha = alpha + + ################################################## + # Blocks + ################################################## + self.filter_single_pole_iir_filter_xx_0 = filter.single_pole_iir_filter_ff(alpha, 1) + self.blocks_sub_xx_1 = blocks.sub_ff(1) + self.blocks_sub_xx_0 = blocks.sub_ff(1) + self.blocks_multiply_xx_2 = blocks.multiply_vff(1) + self.blocks_multiply_xx_1 = blocks.multiply_vff(1) + self.blocks_multiply_xx_0 = blocks.multiply_vff(1) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((2, )) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.blocks_divide_xx_0 = blocks.divide_ff(1) + self.blocks_complex_to_mag_squared_0 = blocks.complex_to_mag_squared(1) + self.blocks_complex_to_float_0 = blocks.complex_to_float(1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_complex_to_float_0, 0), (self.blocks_multiply_xx_0, 0)) + self.connect((self.blocks_complex_to_float_0, 1), (self.blocks_multiply_xx_0, 1)) + self.connect((self.blocks_multiply_xx_0, 0), (self.blocks_divide_xx_0, 0)) + self.connect((self.blocks_sub_xx_0, 0), (self.blocks_float_to_complex_0, 1)) + self.connect((self.blocks_multiply_xx_1, 0), (self.blocks_sub_xx_0, 1)) + self.connect((self.filter_single_pole_iir_filter_xx_0, 0), (self.blocks_multiply_xx_1, 1)) + self.connect((self.blocks_complex_to_float_0, 0), (self.blocks_multiply_xx_1, 0)) + self.connect((self.blocks_multiply_xx_2, 0), (self.blocks_sub_xx_1, 1)) + self.connect((self.blocks_complex_to_float_0, 1), (self.blocks_sub_xx_0, 0)) + self.connect((self.blocks_sub_xx_1, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_complex_to_mag_squared_0, 0), (self.blocks_divide_xx_0, 1)) + self.connect((self.blocks_complex_to_float_0, 0), (self.blocks_sub_xx_1, 0)) + self.connect((self.blocks_divide_xx_0, 0), (self.blocks_multiply_const_vxx_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.filter_single_pole_iir_filter_xx_0, 0)) + self.connect((self, 0), (self.blocks_complex_to_float_0, 0)) + self.connect((self, 0), (self.blocks_complex_to_mag_squared_0, 0)) + self.connect((self.blocks_float_to_complex_0, 0), (self, 0)) + self.connect((self.filter_single_pole_iir_filter_xx_0, 0), (self.blocks_multiply_xx_2, 0)) + self.connect((self.blocks_complex_to_float_0, 1), (self.blocks_multiply_xx_2, 1)) + + +# QT sink close method reimplementation + + def get_alpha(self): + return self.alpha + + def set_alpha(self, alpha): + self.alpha = alpha + self.filter_single_pole_iir_filter_xx_0.set_taps(self.alpha) + + diff --git a/gr-channels/python/channels/phase_noise_gen.py b/gr-channels/python/channels/phase_noise_gen.py new file mode 100644 index 0000000000..95c5676e40 --- /dev/null +++ b/gr-channels/python/channels/phase_noise_gen.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Phase Noise Generator +# Author: mettus +# Generated: Thu Aug 1 11:59:39 2013 +################################################## + +from gnuradio import analog +from gnuradio import blocks +from gnuradio import filter +from gnuradio import gr +from gnuradio.filter import firdes + +class phase_noise_gen(gr.hier_block2): + + def __init__(self, noise_mag=0, alpha=0.1): + gr.hier_block2.__init__( + self, "Phase Noise Generator", + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + gr.io_signature(1, 1, gr.sizeof_gr_complex*1), + ) + + ################################################## + # Parameters + ################################################## + self.noise_mag = noise_mag + self.alpha = alpha + + ################################################## + # Blocks + ################################################## + self.filter_single_pole_iir_filter_xx_0 = filter.single_pole_iir_filter_ff(alpha, 1) + self.blocks_transcendental_0_0 = blocks.transcendental("sin", "float") + self.blocks_transcendental_0 = blocks.transcendental("cos", "float") + self.blocks_multiply_xx_0 = blocks.multiply_vcc(1) + self.blocks_float_to_complex_0 = blocks.float_to_complex(1) + self.analog_noise_source_x_0 = analog.noise_source_f(analog.GR_GAUSSIAN, noise_mag, 42) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_float_to_complex_0, 0), (self.blocks_multiply_xx_0, 1)) + self.connect((self.analog_noise_source_x_0, 0), (self.filter_single_pole_iir_filter_xx_0, 0)) + self.connect((self.blocks_multiply_xx_0, 0), (self, 0)) + self.connect((self, 0), (self.blocks_multiply_xx_0, 0)) + self.connect((self.filter_single_pole_iir_filter_xx_0, 0), (self.blocks_transcendental_0, 0)) + self.connect((self.filter_single_pole_iir_filter_xx_0, 0), (self.blocks_transcendental_0_0, 0)) + self.connect((self.blocks_transcendental_0, 0), (self.blocks_float_to_complex_0, 0)) + self.connect((self.blocks_transcendental_0_0, 0), (self.blocks_float_to_complex_0, 1)) + + +# QT sink close method reimplementation + + def get_noise_mag(self): + return self.noise_mag + + def set_noise_mag(self, noise_mag): + self.noise_mag = noise_mag + self.analog_noise_source_x_0.set_amplitude(self.noise_mag) + + def get_alpha(self): + return self.alpha + + def set_alpha(self, alpha): + self.alpha = alpha + self.filter_single_pole_iir_filter_xx_0.set_taps(self.alpha) + + diff --git a/gr-channels/python/channels/quantizer.py b/gr-channels/python/channels/quantizer.py new file mode 100644 index 0000000000..a3d918c7c2 --- /dev/null +++ b/gr-channels/python/channels/quantizer.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +################################################## +# Gnuradio Python Flow Graph +# Title: Quantizer +# Generated: Thu Aug 1 11:09:51 2013 +################################################## + +from gnuradio import blocks +from gnuradio import gr +from gnuradio.filter import firdes + +class quantizer(gr.hier_block2): + + def __init__(self, bits=16): + gr.hier_block2.__init__( + self, "Quantizer", + gr.io_signature(1, 1, gr.sizeof_float*1), + gr.io_signature(1, 1, gr.sizeof_float*1), + ) + + ################################################## + # Parameters + ################################################## + self.bits = bits + + ################################################## + # Blocks + ################################################## + self.blocks_short_to_float_0 = blocks.short_to_float(1, 1) + self.blocks_multiply_const_vxx_0_0 = blocks.multiply_const_vff((1.0/pow(2.0,bits-1.0), )) + self.blocks_multiply_const_vxx_0 = blocks.multiply_const_vff((pow(2,bits-1.0), )) + self.blocks_float_to_short_0 = blocks.float_to_short(1, 1) + + ################################################## + # Connections + ################################################## + self.connect((self.blocks_multiply_const_vxx_0_0, 0), (self, 0)) + self.connect((self.blocks_short_to_float_0, 0), (self.blocks_multiply_const_vxx_0_0, 0)) + self.connect((self.blocks_float_to_short_0, 0), (self.blocks_short_to_float_0, 0)) + self.connect((self.blocks_multiply_const_vxx_0, 0), (self.blocks_float_to_short_0, 0)) + self.connect((self, 0), (self.blocks_multiply_const_vxx_0, 0)) + + +# QT sink close method reimplementation + + def get_bits(self): + return self.bits + + def set_bits(self, bits): + self.bits = bits + self.blocks_multiply_const_vxx_0_0.set_k((1.0/pow(2.0,self.bits-1.0), )) + self.blocks_multiply_const_vxx_0.set_k((pow(2,self.bits-1.0), )) + + diff --git a/gr-channels/swig/channels_swig.i b/gr-channels/swig/channels_swig.i index fb7ada7dae..784a10a085 100644 --- a/gr-channels/swig/channels_swig.i +++ b/gr-channels/swig/channels_swig.i @@ -29,14 +29,17 @@ %{ #include "gnuradio/channels/channel_model.h" +#include "gnuradio/channels/channel_model2.h" #include "gnuradio/channels/fading_model.h" #include "gnuradio/channels/selective_fading_model.h" %} %include "gnuradio/channels/channel_model.h" +%include "gnuradio/channels/channel_model2.h" %include "gnuradio/channels/fading_model.h" %include "gnuradio/channels/selective_fading_model.h" GR_SWIG_BLOCK_MAGIC2(channels, channel_model); +GR_SWIG_BLOCK_MAGIC2(channels, channel_model2); GR_SWIG_BLOCK_MAGIC2(channels, fading_model); GR_SWIG_BLOCK_MAGIC2(channels, selective_fading_model); diff --git a/gr-digital/examples/narrowband/benchmark_rx.py b/gr-digital/examples/narrowband/benchmark_rx.py index 622773aac5..09d923fc6b 100755 --- a/gr-digital/examples/narrowband/benchmark_rx.py +++ b/gr-digital/examples/narrowband/benchmark_rx.py @@ -50,10 +50,10 @@ class my_top_block(gr.top_block): symbol_rate = options.bitrate / demodulator(**args).bits_per_symbol() self.source = uhd_receiver(options.args, symbol_rate, - options.samples_per_symbol, - options.rx_freq, options.rx_gain, + options.samples_per_symbol, options.rx_freq, + options.lo_offset, options.rx_gain, options.spec, options.antenna, - options.verbose) + options.clock_source, options.verbose) options.samples_per_symbol = self.source._sps elif(options.from_file is not None): diff --git a/gr-digital/examples/narrowband/benchmark_tx.py b/gr-digital/examples/narrowband/benchmark_tx.py index 11ad7ea26e..2cb74d5a77 100755 --- a/gr-digital/examples/narrowband/benchmark_tx.py +++ b/gr-digital/examples/narrowband/benchmark_tx.py @@ -49,10 +49,10 @@ class my_top_block(gr.top_block): symbol_rate = options.bitrate / modulator(**args).bits_per_symbol() self.sink = uhd_transmitter(options.args, symbol_rate, - options.samples_per_symbol, - options.tx_freq, options.tx_gain, + options.samples_per_symbol, options.tx_freq, + options.lo_offset, options.tx_gain, options.spec, options.antenna, - options.verbose) + options.clock_source, options.verbose) options.samples_per_symbol = self.sink._sps elif(options.to_file is not None): diff --git a/gr-digital/examples/narrowband/uhd_interface.py b/gr-digital/examples/narrowband/uhd_interface.py index fe022c731c..474e96f486 100644 --- a/gr-digital/examples/narrowband/uhd_interface.py +++ b/gr-digital/examples/narrowband/uhd_interface.py @@ -42,14 +42,18 @@ def add_freq_option(parser): metavar="FREQ") class uhd_interface: - def __init__(self, istx, args, sym_rate, sps, freq=None, - gain=None, spec=None, antenna=None): + def __init__(self, istx, args, sym_rate, sps, freq=None, lo_offset=None, + gain=None, spec=None, antenna=None, clock_source=None): if(istx): self.u = uhd.usrp_sink(device_addr=args, stream_args=uhd.stream_args('fc32')) else: self.u = uhd.usrp_source(device_addr=args, stream_args=uhd.stream_args('fc32')) + # Set clock source + if(clock_source): + self.u.set_clock_source(clock_source, 0) + # Set the subdevice spec if(spec): self.u.set_subdev_spec(spec, 0) @@ -62,9 +66,10 @@ class uhd_interface: self._ant = antenna self._spec = spec self._gain = self.set_gain(gain) - self._freq = self.set_freq(freq) - + self._lo_offset = lo_offset + self._freq = self.set_freq(freq, lo_offset) self._rate, self._sps = self.set_sample_rate(sym_rate, sps) + self._clock_source = clock_source def set_sample_rate(self, sym_rate, req_sps): start_sps = req_sps @@ -107,12 +112,12 @@ class uhd_interface: self.u.set_gain(gain, 0) return gain - def set_freq(self, freq=None): + def set_freq(self, freq=None, lo_offset=None): if(freq is None): sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") sys.exit(1) - r = self.u.set_center_freq(freq, 0) + r = self.u.set_center_freq(uhd.tune_request(freq, lo_offset)) if r: return freq else: @@ -126,15 +131,15 @@ class uhd_interface: #-------------------------------------------------------------------# class uhd_transmitter(uhd_interface, gr.hier_block2): - def __init__(self, args, sym_rate, sps, freq=None, gain=None, - spec=None, antenna=None, verbose=False): + def __init__(self, args, sym_rate, sps, freq=None, lo_offset=None, gain=None, + spec=None, antenna=None, clock_source=None, verbose=False): gr.hier_block2.__init__(self, "uhd_transmitter", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(0,0,0)) # Set up the UHD interface as a transmitter uhd_interface.__init__(self, True, args, sym_rate, sps, - freq, gain, spec, antenna) + freq, lo_offset, gain, spec, antenna, clock_source) self.connect(self, self.u) @@ -152,8 +157,12 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): parser.add_option("", "--tx-freq", type="eng_float", default=None, help="set transmit frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("", "--lo-offset", type="eng_float", default=0, + help="set local oscillator offset in Hz (default is 0)") parser.add_option("", "--tx-gain", type="eng_float", default=None, help="set transmit gain in dB (default is midpoint)") + parser.add_option("-C", "--clock-source", type="string", default=None, + help="select clock source (e.g. 'external') [default=%default]") parser.add_option("-v", "--verbose", action="store_true", default=False) # Make a static method to call before instantiation @@ -166,11 +175,12 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): print "\nUHD Transmitter:" print "Args: %s" % (self._args) print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "LO Offset: %sHz" % (eng_notation.num_to_str(self._lo_offset)) print "Gain: %f dB" % (self._gain) print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) print "Antenna: %s" % (self._ant) - print "Subdev Sec: %s" % (self._spec) - + print "Subdev Spec: %s" % (self._spec) + print "Clock Source: %s" % (self._clock_source) #-------------------------------------------------------------------# # RECEIVER @@ -178,15 +188,15 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): class uhd_receiver(uhd_interface, gr.hier_block2): - def __init__(self, args, sym_rate, sps, freq=None, gain=None, - spec=None, antenna=None, verbose=False): + def __init__(self, args, sym_rate, sps, freq=None, lo_offset=None, gain=None, + spec=None, antenna=None, clock_source=None, verbose=False): gr.hier_block2.__init__(self, "uhd_receiver", gr.io_signature(0,0,0), gr.io_signature(1,1,gr.sizeof_gr_complex)) # Set up the UHD interface as a receiver uhd_interface.__init__(self, False, args, sym_rate, sps, - freq, gain, spec, antenna) + freq, lo_offset, gain, spec, antenna, clock_source) self.connect(self.u, self) @@ -204,8 +214,12 @@ class uhd_receiver(uhd_interface, gr.hier_block2): parser.add_option("", "--rx-freq", type="eng_float", default=None, help="set receive frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("", "--lo-offset", type="eng_float", default=0, + help="set local oscillator offset in Hz (default is 0)") parser.add_option("", "--rx-gain", type="eng_float", default=None, help="set receive gain in dB (default is midpoint)") + parser.add_option("-C", "--clock-source", type="string", default=None, + help="select clock source (e.g. 'external') [default=%default]") if not parser.has_option("--verbose"): parser.add_option("-v", "--verbose", action="store_true", default=False) @@ -217,9 +231,12 @@ class uhd_receiver(uhd_interface, gr.hier_block2): Prints information about the UHD transmitter """ print "\nUHD Receiver:" - print "UHD Args: %s" % (self._args) - print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) - print "Gain: %f dB" % (self._gain) - print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) - print "Antenna: %s" % (self._ant) - print "Spec: %s" % (self._spec) + print "UHD Args: %s" % (self._args) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "LO Offset: %sHz" % (eng_notation.num_to_str(self._lo_offset)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + print "Spec: %s" % (self._spec) + print "Clock Source: %s" % (self._clock_source) + diff --git a/gr-digital/examples/ofdm/benchmark_rx.py b/gr-digital/examples/ofdm/benchmark_rx.py index dbcd866ad2..fdf81176ac 100755 --- a/gr-digital/examples/ofdm/benchmark_rx.py +++ b/gr-digital/examples/ofdm/benchmark_rx.py @@ -40,10 +40,10 @@ class my_top_block(gr.top_block): if(options.rx_freq is not None): self.source = uhd_receiver(options.args, - options.bandwidth, - options.rx_freq, options.rx_gain, + options.bandwidth, options.rx_freq, + options.lo_offset, options.rx_gain, options.spec, options.antenna, - options.verbose) + options.clock_source, options.verbose) elif(options.from_file is not None): self.source = blocks.file_source(gr.sizeof_gr_complex, options.from_file) else: diff --git a/gr-digital/examples/ofdm/benchmark_tx.py b/gr-digital/examples/ofdm/benchmark_tx.py index 2942178ccf..47dc80964a 100755 --- a/gr-digital/examples/ofdm/benchmark_tx.py +++ b/gr-digital/examples/ofdm/benchmark_tx.py @@ -39,10 +39,10 @@ class my_top_block(gr.top_block): if(options.tx_freq is not None): self.sink = uhd_transmitter(options.args, - options.bandwidth, - options.tx_freq, options.tx_gain, + options.bandwidth, options.tx_freq, + options.lo_offset, options.tx_gain, options.spec, options.antenna, - options.verbose) + options.clock_source, options.verbose) elif(options.to_file is not None): self.sink = blocks.file_sink(gr.sizeof_gr_complex, options.to_file) else: @@ -114,7 +114,8 @@ def main(): pktno += 1 send_pkt(eof=True) - tb.wait() # wait for it to finish + time.sleep(2) # allow time for queued packets to be sent + tb.wait() # wait for it to finish if __name__ == '__main__': try: diff --git a/gr-digital/examples/ofdm/uhd_interface.py b/gr-digital/examples/ofdm/uhd_interface.py index e4b13955dc..5b4d707b07 100644 --- a/gr-digital/examples/ofdm/uhd_interface.py +++ b/gr-digital/examples/ofdm/uhd_interface.py @@ -42,14 +42,18 @@ def add_freq_option(parser): metavar="FREQ") class uhd_interface: - def __init__(self, istx, args, bandwidth, freq=None, - gain=None, spec=None, antenna=None): + def __init__(self, istx, args, bandwidth, freq=None, lo_offset=None, + gain=None, spec=None, antenna=None, clock_source=None): if(istx): self.u = uhd.usrp_sink(device_addr=args, stream_args=uhd.stream_args('fc32')) else: self.u = uhd.usrp_source(device_addr=args, stream_args=uhd.stream_args('fc32')) + # Set clock source to external. + if(clock_source): + self.u.set_clock_source(clock_source, 0) + # Set the subdevice spec if(spec): self.u.set_subdev_spec(spec, 0) @@ -62,9 +66,10 @@ class uhd_interface: self._ant = antenna self._spec = spec self._gain = self.set_gain(gain) - self._freq = self.set_freq(freq) - + self._lo_offset = lo_offset + self._freq = self.set_freq(freq, lo_offset) self._rate = self.set_sample_rate(bandwidth) + self._clock_source = clock_source def set_sample_rate(self, bandwidth): self.u.set_samp_rate(bandwidth) @@ -87,12 +92,13 @@ class uhd_interface: self.u.set_gain(gain, 0) return gain - def set_freq(self, freq=None): + def set_freq(self, freq=None, lo_offset=None): if(freq is None): sys.stderr.write("You must specify -f FREQ or --freq FREQ\n") sys.exit(1) - r = self.u.set_center_freq(freq, 0) + r = self.u.set_center_freq(uhd.tune_request(freq, lo_offset)) + if r: return freq else: @@ -106,15 +112,15 @@ class uhd_interface: #-------------------------------------------------------------------# class uhd_transmitter(uhd_interface, gr.hier_block2): - def __init__(self, args, bandwidth, freq=None, gain=None, - spec=None, antenna=None, verbose=False): + def __init__(self, args, bandwidth, freq=None, lo_offset=None, gain=None, + spec=None, antenna=None, clock_source=None, verbose=False): gr.hier_block2.__init__(self, "uhd_transmitter", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(0,0,0)) # Set up the UHD interface as a transmitter uhd_interface.__init__(self, True, args, bandwidth, - freq, gain, spec, antenna) + freq, lo_offset, gain, spec, antenna, clock_source) self.connect(self, self.u) @@ -132,8 +138,12 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): parser.add_option("", "--tx-freq", type="eng_float", default=None, help="set transmit frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("", "--lo-offset", type="eng_float", default=0, + help="set local oscillator offset in Hz (default is 0)") parser.add_option("", "--tx-gain", type="eng_float", default=None, help="set transmit gain in dB (default is midpoint)") + parser.add_option("-C", "--clock-source", type="string", default=None, + help="select clock source (e.g. 'external') [default=%default]") parser.add_option("-v", "--verbose", action="store_true", default=False) # Make a static method to call before instantiation @@ -144,12 +154,14 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): Prints information about the UHD transmitter """ print "\nUHD Transmitter:" - print "UHD Args: %s" % (self._args) - print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) - print "Gain: %f dB" % (self._gain) - print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) - print "Antenna: %s" % (self._ant) - print "Subdev Sec: %s" % (self._spec) + print "UHD Args: %s" % (self._args) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "LO Offset: %sHz" % (eng_notation.num_to_str(self._lo_offset)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + print "Subdev Sec: %s" % (self._spec) + print "Clock Source: %s" % (self._clock_source) @@ -159,15 +171,15 @@ class uhd_transmitter(uhd_interface, gr.hier_block2): class uhd_receiver(uhd_interface, gr.hier_block2): - def __init__(self, args, bandwidth, freq=None, gain=None, - spec=None, antenna=None, verbose=False): + def __init__(self, args, bandwidth, freq=None, lo_offset=None, gain=None, + spec=None, antenna=None, clock_source=None, verbose=False): gr.hier_block2.__init__(self, "uhd_receiver", gr.io_signature(0,0,0), gr.io_signature(1,1,gr.sizeof_gr_complex)) # Set up the UHD interface as a receiver uhd_interface.__init__(self, False, args, bandwidth, - freq, gain, spec, antenna) + freq, lo_offset, gain, spec, antenna, clock_source) self.connect(self.u, self) @@ -185,8 +197,12 @@ class uhd_receiver(uhd_interface, gr.hier_block2): parser.add_option("", "--rx-freq", type="eng_float", default=None, help="set receive frequency to FREQ [default=%default]", metavar="FREQ") + parser.add_option("", "--lo-offset", type="eng_float", default=0, + help="set local oscillator offset in Hz (default is 0)") parser.add_option("", "--rx-gain", type="eng_float", default=None, help="set receive gain in dB (default is midpoint)") + parser.add_option("-C", "--clock-source", type="string", default=None, + help="select clock source (e.g. 'external') [default=%default]") if not parser.has_option("--verbose"): parser.add_option("-v", "--verbose", action="store_true", default=False) @@ -198,10 +214,12 @@ class uhd_receiver(uhd_interface, gr.hier_block2): Prints information about the UHD transmitter """ print "\nUHD Receiver:" - print "UHD Args: %s" % (self._args) - print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) - print "Gain: %f dB" % (self._gain) - print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) - print "Antenna: %s" % (self._ant) - print "Subdev Sec: %s" % (self._spec) + print "UHD Args: %s" % (self._args) + print "Freq: %sHz" % (eng_notation.num_to_str(self._freq)) + print "LO Offset: %sHz" % (eng_notation.num_to_str(self._lo_offset)) + print "Gain: %f dB" % (self._gain) + print "Sample Rate: %ssps" % (eng_notation.num_to_str(self._rate)) + print "Antenna: %s" % (self._ant) + print "Subdev Sec: %s" % (self._spec) + print "Clock Source: %s" % (self._clock_source) diff --git a/gr-digital/include/gnuradio/digital/constellation.h b/gr-digital/include/gnuradio/digital/constellation.h index f8b99bb9c5..6ee274dd04 100644 --- a/gr-digital/include/gnuradio/digital/constellation.h +++ b/gr-digital/include/gnuradio/digital/constellation.h @@ -154,7 +154,15 @@ namespace gr { public: typedef boost::shared_ptr<constellation_calcdist> sptr; - // public constructor + /*! + * Make a general constellation object that calculates the Euclidean distance for hard decisions. + * + * \param constell List of constellation points (order of list matches pre_diff_code) + * \param pre_diff_code List of alphabet symbols (before applying any differential + * coding) (order of list matches constell) + * \param rotational_symmetry Number of rotations around unit circle that have the same representation. + * \param dimensionality Number of dimensions to the constellation. + */ static sptr make(std::vector<gr_complex> constell, std::vector<int> pre_diff_code, unsigned int rotational_symmetry, @@ -236,7 +244,18 @@ namespace gr { public: typedef boost::shared_ptr<constellation_rect> sptr; - // public constructor + /*! + * Make a rectangular constellation object. + * + * \param constell List of constellation points (order of list matches pre_diff_code) + * \param pre_diff_code List of alphabet symbols (before applying any differential + * coding) (order of list matches constell) + * \param rotational_symmetry Number of rotations around unit circle that have the same representation. + * \param real_sectors Number of sectors the real axis is split in to. + * \param imag_sectors Number of sectors the imag axis is split in to. + * \param width_real_sectors width of each real sector to calculate decision boundaries. + * \param width_imag_sectors width of each imag sector to calculate decision boundaries. + */ static constellation_rect::sptr make(std::vector<gr_complex> constell, std::vector<int> pre_diff_code, unsigned int rotational_symmetry, diff --git a/gr-filter/grc/filter_fractional_resampler_xx.xml b/gr-filter/grc/filter_fractional_resampler_xx.xml index 48d624d432..40957b889d 100644 --- a/gr-filter/grc/filter_fractional_resampler_xx.xml +++ b/gr-filter/grc/filter_fractional_resampler_xx.xml @@ -39,6 +39,11 @@ <name>in</name> <type>$type</type> </sink> + <sink> + <name>rate</name> + <type>float</type> + <optional>1</optional> + </sink> <source> <name>out</name> <type>$type</type> diff --git a/gr-filter/lib/fractional_resampler_cc_impl.cc b/gr-filter/lib/fractional_resampler_cc_impl.cc index 27f139d813..b17e13638b 100644 --- a/gr-filter/lib/fractional_resampler_cc_impl.cc +++ b/gr-filter/lib/fractional_resampler_cc_impl.cc @@ -41,8 +41,8 @@ namespace gr { fractional_resampler_cc_impl::fractional_resampler_cc_impl (float phase_shift, float resamp_ratio) : block("fractional_resampler_cc", - io_signature::make(1, 1, sizeof(gr_complex)), - io_signature::make(1, 1, sizeof(gr_complex))), + io_signature::make2(1, 2, sizeof(gr_complex), sizeof(float)), + io_signature::make(1, 1, sizeof(gr_complex))), d_mu(phase_shift), d_mu_inc(resamp_ratio), d_resamp(new mmse_fir_interpolator_cc()) { @@ -82,19 +82,38 @@ namespace gr { int ii = 0; // input index int oo = 0; // output index - while(oo < noutput_items) { - out[oo++] = d_resamp->interpolate(&in[ii], d_mu); + if(ninput_items.size() == 1) { + while(oo < noutput_items) { + out[oo++] = d_resamp->interpolate(&in[ii], d_mu); - double s = d_mu + d_mu_inc; - double f = floor(s); - int incr = (int)f; - d_mu = s - f; - ii += incr; - } + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } - consume_each(ii); + consume_each(ii); + return noutput_items; + } - return noutput_items; + else { + const float *rr = (const float*)input_items[1]; + while(oo < noutput_items) { + out[oo++] = d_resamp->interpolate(&in[ii], d_mu); + d_mu_inc = rr[ii]; + + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } + + set_relative_rate(1.0 / d_mu_inc); + consume_each(ii); + return noutput_items; + } } float diff --git a/gr-filter/lib/fractional_resampler_ff_impl.cc b/gr-filter/lib/fractional_resampler_ff_impl.cc index 61950d92c0..131ccaae4e 100644 --- a/gr-filter/lib/fractional_resampler_ff_impl.cc +++ b/gr-filter/lib/fractional_resampler_ff_impl.cc @@ -41,8 +41,8 @@ namespace gr { fractional_resampler_ff_impl::fractional_resampler_ff_impl (float phase_shift, float resamp_ratio) : block("fractional_resampler_ff", - io_signature::make(1, 1, sizeof(float)), - io_signature::make(1, 1, sizeof(float))), + io_signature::make(1, 2, sizeof(float)), + io_signature::make(1, 1, sizeof(float))), d_mu (phase_shift), d_mu_inc (resamp_ratio), d_resamp(new mmse_fir_interpolator_ff()) { @@ -82,19 +82,37 @@ namespace gr { int ii = 0; // input index int oo = 0; // output index - while(oo < noutput_items) { - out[oo++] = d_resamp->interpolate(&in[ii], d_mu); + if(ninput_items.size() == 1) { + while(oo < noutput_items) { + out[oo++] = d_resamp->interpolate(&in[ii], d_mu); - double s = d_mu + d_mu_inc; - double f = floor(s); - int incr = (int)f; - d_mu = s - f; - ii += incr; - } - - consume_each(ii); + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } - return noutput_items; + consume_each(ii); + return noutput_items; + } + else { + const float *rr = (const float*)input_items[1]; + while(oo < noutput_items) { + out[oo++] = d_resamp->interpolate(&in[ii], d_mu); + d_mu_inc = rr[ii]; + + double s = d_mu + d_mu_inc; + double f = floor(s); + int incr = (int)f; + d_mu = s - f; + ii += incr; + } + + set_relative_rate(1.0 / d_mu_inc); + consume_each(ii); + return noutput_items; + } } float diff --git a/gr-filter/python/filter/qa_fractional_resampler.py b/gr-filter/python/filter/qa_fractional_resampler.py index 220ebd9e30..ea01cf7eef 100644 --- a/gr-filter/python/filter/qa_fractional_resampler.py +++ b/gr-filter/python/filter/qa_fractional_resampler.py @@ -35,6 +35,10 @@ def sig_source_c(samp_rate, freq, amp, N): 1j*math.sin(2.*math.pi*freq*x), t) return y +def const_source_f(amp, N): + y = N*[amp,] + return y + class test_fractional_resampler(gr_unittest.TestCase): def setUp(self): @@ -68,7 +72,6 @@ class test_fractional_resampler(gr_unittest.TestCase): self.assertFloatTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) - def test_002_cc(self): N = 10000 # number of samples to use fs = 1000 # baseband sampling rate @@ -95,6 +98,62 @@ class test_fractional_resampler(gr_unittest.TestCase): self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + def test_003_ff(self): + N = 10000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + freq = 10 + data = sig_source_f(fs, freq, 1, N) + ctrl = const_source_f(rrate, N) + signal = blocks.vector_source_f(data) + control = blocks.vector_source_f(ctrl) + op = filter.fractional_resampler_ff(0, 1) + snk = blocks.vector_sink_f() + + self.tb.connect(signal, op, snk) + self.tb.connect(control, (op,1)) + self.tb.run() + + Ntest = 5000 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs/rrate), xrange(L)) + + phase = 0.1884 + expected_data = map(lambda x: math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + + self.assertFloatTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) + + def test_004_cc(self): + N = 10000 # number of samples to use + fs = 1000 # baseband sampling rate + rrate = 1.123 # resampling rate + + freq = 10 + data = sig_source_c(fs, freq, 1, N) + ctrl = const_source_f(rrate, N) + signal = blocks.vector_source_c(data) + control = blocks.vector_source_f(ctrl) + op = filter.fractional_resampler_cc(0.0, 1) + snk = blocks.vector_sink_c() + + self.tb.connect(signal, op, snk) + self.tb.connect(control, (op,1)) + self.tb.run() + + Ntest = 5000 + L = len(snk.data()) + t = map(lambda x: float(x)/(fs/rrate), xrange(L)) + + phase = 0.1884 + expected_data = map(lambda x: math.cos(2.*math.pi*freq*x+phase) + \ + 1j*math.sin(2.*math.pi*freq*x+phase), t) + + dst_data = snk.data() + + self.assertComplexTuplesAlmostEqual(expected_data[-Ntest:], dst_data[-Ntest:], 3) if __name__ == '__main__': gr_unittest.run(test_fractional_resampler, "test_fractional_resampler.xml") diff --git a/gr-qtgui/grc/qtgui_block_tree.xml b/gr-qtgui/grc/qtgui_block_tree.xml index e9d57a664c..14516df71e 100644 --- a/gr-qtgui/grc/qtgui_block_tree.xml +++ b/gr-qtgui/grc/qtgui_block_tree.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- - Copyright 2012 Free Software Foundation, Inc. + Copyright 2012-2013 Free Software Foundation, Inc. This file is part of GNU Radio @@ -37,6 +37,7 @@ <block>qtgui_const_sink_x</block> <block>qtgui_waterfall_sink_x</block> <block>qtgui_time_raster_sink_x</block> + <block>qtgui_histogram_sink_x</block> <block>qtgui_sink_x</block> </cat> </cat> diff --git a/gr-qtgui/grc/qtgui_histogram_sink_x.xml b/gr-qtgui/grc/qtgui_histogram_sink_x.xml new file mode 100644 index 0000000000..8a810abab7 --- /dev/null +++ b/gr-qtgui/grc/qtgui_histogram_sink_x.xml @@ -0,0 +1,91 @@ +<?xml version="1.0"?> +<!-- +################################################### +##QT GUI Histogram Sink +################################################### + --> +<block> + <name>QT GUI Histogram Sink</name> + <key>qtgui_histogram_sink_x</key> + <import>from PyQt4 import Qt</import> + <import>from gnuradio import qtgui</import> + <import>import sip</import> + <make>#set $win = 'self._%s_win'%$id +qtgui.histogram_sink_f( + $size, + $bins, + $xmin, + $xmax, + $name, + $nconnections +) +self.$(id).set_update_time($update_time) +self._$(id)_win = sip.wrapinstance(self.$(id).pyqwidget(), Qt.QWidget) +$(gui_hint()($win))</make> + <callback>set_update_time($update_time)</callback> + <callback>set_title($which, $title)</callback> + <callback>set_color($which, $color)</callback> + <callback>set_bins($bins)</callback> + <callback>set_x_axis($bins)</callback> + <param> + <name>Name</name> + <key>name</key> + <value>QT GUI Plot</value> + <type>string</type> + </param> + <param> + <name>Number of Points</name> + <key>size</key> + <value>1024</value> + <type>int</type> + </param> + <param> + <name>Number of Bins</name> + <key>bins</key> + <value>100</value> + <type>int</type> + </param> + <param> + <name>Min x-axis</name> + <key>xmin</key> + <value>-1</value> + <type>real</type> + </param> + <param> + <name>Max x-axis</name> + <key>xmax</key> + <value>1</value> + <type>real</type> + </param> + <param> + <name>Number of Inputs</name> + <key>nconnections</key> + <value>1</value> + <type>int</type> + <hide>part</hide> + </param> + <param> + <name>Update Period</name> + <key>update_time</key> + <value>0.10</value> + <type>float</type> + <hide>part</hide> + </param> + <param> + <name>GUI Hint</name> + <key>gui_hint</key> + <value></value> + <type>gui_hint</type> + <hide>part</hide> + </param> + <sink> + <name>in</name> + <type>float</type> + <nports>$nconnections</nports> + </sink> + <doc> +The GUI hint can be used to position the widget within the application. \ +The hint is of the form [tab_id@tab_index]: [row, col, row_span, col_span]. \ +Both the tab specification and the grid position are optional. + </doc> +</block> diff --git a/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt b/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt index 8f95837fec..0d5cb58f5d 100644 --- a/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt +++ b/gr-qtgui/include/gnuradio/qtgui/CMakeLists.txt @@ -33,6 +33,9 @@ install(FILES freq_sink_c.h freq_sink_f.h FrequencyDisplayPlot.h + histogram_sink_f.h + histogramdisplayform.h + HistogramDisplayPlot.h plot_raster.h plot_waterfall.h qtgui_types.h diff --git a/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h b/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h index bafc712aac..895328ad6e 100644 --- a/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h +++ b/gr-qtgui/include/gnuradio/qtgui/DisplayPlot.h @@ -180,20 +180,21 @@ public: // void PlotNewData(...); public slots: - void setYaxis(double min, double max); - void setXaxis(double min, double max); - void setLineLabel(int which, QString label); - QString getLineLabel(int which); - void setLineColor(int which, QColor color); - QColor getLineColor(int which) const; - void setLineWidth(int which, int width); - int getLineWidth(int which) const; - void setLineStyle(int which, Qt::PenStyle style); - const Qt::PenStyle getLineStyle(int which) const; - void setLineMarker(int which, QwtSymbol::Style marker); - const QwtSymbol::Style getLineMarker(int which) const; - void setMarkerAlpha(int which, int alpha); - int getMarkerAlpha(int which) const; + virtual void setYaxis(double min, double max); + virtual void setXaxis(double min, double max); + virtual void setLineLabel(int which, QString label); + virtual QString getLineLabel(int which); + virtual void setLineColor(int which, QColor color); + virtual QColor getLineColor(int which) const; + virtual void setLineWidth(int which, int width); + virtual int getLineWidth(int which) const; + virtual void setLineStyle(int which, Qt::PenStyle style); + virtual const Qt::PenStyle getLineStyle(int which) const; + virtual void setLineMarker(int which, QwtSymbol::Style marker); + virtual const QwtSymbol::Style getLineMarker(int which) const; + virtual void setMarkerAlpha(int which, int alpha); + virtual int getMarkerAlpha(int which) const; + // Need a function for each curve for setting via stylesheet. // Can't use preprocessor directives because we're inside a Q_OBJECT. void setLineColor1 (QColor); diff --git a/gr-qtgui/include/gnuradio/qtgui/HistogramDisplayPlot.h b/gr-qtgui/include/gnuradio/qtgui/HistogramDisplayPlot.h new file mode 100644 index 0000000000..bcf7272691 --- /dev/null +++ b/gr-qtgui/include/gnuradio/qtgui/HistogramDisplayPlot.h @@ -0,0 +1,74 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef HISTOGRAM_DISPLAY_PLOT_H +#define HISTOGRAM_DISPLAY_PLOT_H + +#include <stdint.h> +#include <cstdio> +#include <vector> +#include <gnuradio/qtgui/DisplayPlot.h> + +/*! + * \brief QWidget for displaying time domain plots. + * \ingroup qtgui_blk + */ +class HistogramDisplayPlot: public DisplayPlot +{ + Q_OBJECT + +public: + HistogramDisplayPlot(int nplots, QWidget*); + virtual ~HistogramDisplayPlot(); + + void plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, const double timeInterval); + + void replot(); + +public slots: + void setAutoScale(bool state); + void setSemilogx(bool en); + void setSemilogy(bool en); + void setAccumulate(bool en); + + void setMarkerAlpha(int which, int alpha); + int getMarkerAlpha(int which) const; + void setLineColor(int which, QColor color); + + void setNumBins(int bins); + +private: + void _resetXAxisPoints(double bottom, double top); + void _autoScale(double bottom, double top); + + double* _xAxisPoints; + std::vector<double*> _yDataPoints; + + int _bins; + bool _accum; + + bool d_semilogx; + bool d_semilogy; +}; + +#endif /* HISTOGRAM_DISPLAY_PLOT_H */ diff --git a/gr-qtgui/include/gnuradio/qtgui/displayform.h b/gr-qtgui/include/gnuradio/qtgui/displayform.h index f94bb7fd04..54f878dc47 100644 --- a/gr-qtgui/include/gnuradio/qtgui/displayform.h +++ b/gr-qtgui/include/gnuradio/qtgui/displayform.h @@ -77,7 +77,7 @@ public slots: QwtSymbol::Style lineMarker(int which); int markerAlpha(int which); - virtual void setSampleRate(const QString &rate) = 0; + virtual void setSampleRate(const QString &rate); void setStop(bool on); void setStop(); diff --git a/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h b/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h new file mode 100644 index 0000000000..82fbca64f5 --- /dev/null +++ b/gr-qtgui/include/gnuradio/qtgui/histogram_sink_f.h @@ -0,0 +1,108 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_HISTOGRAM_SINK_F_H +#define INCLUDED_QTGUI_HISTOGRAM_SINK_F_H + +#include <Python.h> +#include <gnuradio/qtgui/api.h> +#include <gnuradio/sync_block.h> +#include <qapplication.h> + +namespace gr { + namespace qtgui { + + /*! + * \brief A graphical sink to display a histogram. + * \ingroup instrumentation_blk + * \ingroup qtgui_blk + * + * \details + * This is a QT-based graphical sink the displays a histogram of + * the data. + */ + class QTGUI_API histogram_sink_f : virtual public sync_block + { + public: + // gr::qtgui::histogram_sink_f::sptr + typedef boost::shared_ptr<histogram_sink_f> sptr; + + /*! + * \brief Build floating point histogram sink + * + * \param size number of points to plot at once + * \param bins number of bins to sort the data into + * \param xmin minimum x-axis value + * \param xmax maximum x-axis value + * \param name title for the plot + * \param nconnections number of signals connected to sink + * \param parent a QWidget parent object, if any + */ + static sptr make(int size, int bins, + double xmin, double xmax, + const std::string &name, + int nconnections=1, + QWidget *parent=NULL); + + virtual void exec_() = 0; + virtual PyObject* pyqwidget() = 0; + + virtual void set_y_axis(double min, double max) = 0; + virtual void set_x_axis(double min, double max) = 0; + virtual void set_update_time(double t) = 0; + virtual void set_title(const std::string &title) = 0; + virtual void set_line_label(int which, const std::string &line) = 0; + virtual void set_line_color(int which, const std::string &color) = 0; + virtual void set_line_width(int which, int width) = 0; + virtual void set_line_style(int which, int style) = 0; + virtual void set_line_marker(int which, int marker) = 0; + virtual void set_line_alpha(int which, double alpha) = 0; + virtual void set_nsamps(const int newsize) = 0; + virtual void set_bins(const int bins) = 0; + + virtual std::string title() = 0; + virtual std::string line_label(int which) = 0; + virtual std::string line_color(int which) = 0; + virtual int line_width(int which) = 0; + virtual int line_style(int which) = 0; + virtual int line_marker(int which) = 0; + virtual double line_alpha(int which) = 0; + + virtual void set_size(int width, int height) = 0; + + virtual void enable_menu(bool en=true) = 0; + virtual void enable_grid(bool en=true) = 0; + virtual void enable_autoscale(bool en=true) = 0; + virtual void enable_semilogx(bool en=true) = 0; + virtual void enable_semilogy(bool en=true) = 0; + virtual void enable_accumulate(bool en=true) = 0; + virtual int nsamps() const = 0; + virtual int bins() const = 0; + virtual void reset() = 0; + + QApplication *d_qApplication; + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_HISTOGRAM_SINK_F_H */ diff --git a/gr-qtgui/include/gnuradio/qtgui/histogramdisplayform.h b/gr-qtgui/include/gnuradio/qtgui/histogramdisplayform.h new file mode 100644 index 0000000000..b1a242ed99 --- /dev/null +++ b/gr-qtgui/include/gnuradio/qtgui/histogramdisplayform.h @@ -0,0 +1,80 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef HISTOGRAM_DISPLAY_FORM_H +#define HISTOGRAM_DISPLAY_FORM_H + +#include <gnuradio/qtgui/spectrumUpdateEvents.h> +#include <gnuradio/qtgui/HistogramDisplayPlot.h> +#include <QtGui/QtGui> +#include <vector> + +#include <gnuradio/qtgui/displayform.h> + +/*! + * \brief DisplayForm child for managing histogram domain plots. + * \ingroup qtgui_blk + */ +class HistogramDisplayForm : public DisplayForm +{ + Q_OBJECT + + public: + HistogramDisplayForm(int nplots=1, QWidget* parent = 0); + ~HistogramDisplayForm(); + + HistogramDisplayPlot* getPlot(); + + int getNPoints() const; + +public slots: + void customEvent(QEvent * e); + + void setYaxis(double min, double max); + void setXaxis(double min, double max); + void setNPoints(const int); + void autoScale(bool en); + void setSemilogx(bool en); + void setSemilogy(bool en); + + void setNumBins(const int); + void setAccumulate(bool en); + +private slots: + void newData(const QEvent*); + +private: + QIntValidator* _intValidator; + + double _startFrequency; + double _stopFrequency; + + int d_npoints; + + bool d_semilogx; + bool d_semilogy; + + QAction *d_semilogxmenu; + QAction *d_semilogymenu; +}; + +#endif /* HISTOGRAM_DISPLAY_FORM_H */ diff --git a/gr-qtgui/include/gnuradio/qtgui/spectrumUpdateEvents.h b/gr-qtgui/include/gnuradio/qtgui/spectrumUpdateEvents.h index f3bb486d82..c1dbdafc26 100644 --- a/gr-qtgui/include/gnuradio/qtgui/spectrumUpdateEvents.h +++ b/gr-qtgui/include/gnuradio/qtgui/spectrumUpdateEvents.h @@ -262,4 +262,32 @@ private: }; +/********************************************************************/ + + +class HistogramUpdateEvent: public QEvent +{ +public: + HistogramUpdateEvent(const std::vector<double*> points, + const uint64_t npoints); + + ~HistogramUpdateEvent(); + + int which() const; + const std::vector<double*> getDataPoints() const; + uint64_t getNumDataPoints() const; + bool getRepeatDataFlag() const; + + static QEvent::Type Type() + { return QEvent::Type(SpectrumUpdateEventType); } + +protected: + +private: + size_t _nplots; + std::vector<double*> _points; + uint64_t _npoints; +}; + + #endif /* SPECTRUM_UPDATE_EVENTS_H */ diff --git a/gr-qtgui/lib/CMakeLists.txt b/gr-qtgui/lib/CMakeLists.txt index 2df8b31555..257f03a2a2 100644 --- a/gr-qtgui/lib/CMakeLists.txt +++ b/gr-qtgui/lib/CMakeLists.txt @@ -29,6 +29,7 @@ set(qtgui_moc_hdrs ${qtgui_mod_includedir}/freqdisplayform.h ${qtgui_mod_includedir}/constellationdisplayform.h ${qtgui_mod_includedir}/waterfalldisplayform.h + ${qtgui_mod_includedir}/histogramdisplayform.h ${qtgui_mod_includedir}/form_menus.h ${qtgui_mod_includedir}/DisplayPlot.h ${qtgui_mod_includedir}/FrequencyDisplayPlot.h @@ -36,6 +37,7 @@ set(qtgui_moc_hdrs ${qtgui_mod_includedir}/TimeRasterDisplayPlot.h ${qtgui_mod_includedir}/WaterfallDisplayPlot.h ${qtgui_mod_includedir}/ConstellationDisplayPlot.h + ${qtgui_mod_includedir}/HistogramDisplayPlot.h ) QT4_WRAP_CPP(qtgui_moc_srcs ${qtgui_moc_hdrs}) QT4_WRAP_UI(qtgui_ui_hdrs spectrumdisplayform.ui) @@ -58,12 +60,14 @@ set(qtgui_srcs WaterfallDisplayPlot.cc waterfallGlobalData.cc ConstellationDisplayPlot.cc + HistogramDisplayPlot.cc spectrumdisplayform.cc displayform.cc timedisplayform.cc timerasterdisplayform.cc freqdisplayform.cc constellationdisplayform.cc + histogramdisplayform.cc waterfalldisplayform.cc SpectrumGUIClass.cc spectrumUpdateEvents.cc @@ -80,6 +84,7 @@ set(qtgui_srcs const_sink_c_impl.cc waterfall_sink_c_impl.cc waterfall_sink_f_impl.cc + histogram_sink_f_impl.cc qtgui_util.cc ) diff --git a/gr-qtgui/lib/ConstellationDisplayPlot.cc b/gr-qtgui/lib/ConstellationDisplayPlot.cc index f483867776..f14016743f 100644 --- a/gr-qtgui/lib/ConstellationDisplayPlot.cc +++ b/gr-qtgui/lib/ConstellationDisplayPlot.cc @@ -80,6 +80,9 @@ ConstellationDisplayPlot::ConstellationDisplayPlot(int nplots, QWidget* parent) _zoomer->setRubberBandPen(c); _zoomer->setTrackerPen(c); + _magnifier->setAxisEnabled(QwtPlot::xBottom, true); + _magnifier->setAxisEnabled(QwtPlot::yLeft, true); + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); set_xaxis(-2.0, 2.0); setAxisTitle(QwtPlot::xBottom, "In-phase"); diff --git a/gr-qtgui/lib/HistogramDisplayPlot.cc b/gr-qtgui/lib/HistogramDisplayPlot.cc new file mode 100644 index 0000000000..bcdab6b3f4 --- /dev/null +++ b/gr-qtgui/lib/HistogramDisplayPlot.cc @@ -0,0 +1,440 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef HISTOGRAM_DISPLAY_PLOT_C +#define HISTOGRAM_DISPLAY_PLOT_C + +#include <gnuradio/qtgui/HistogramDisplayPlot.h> + +#include <qwt_scale_draw.h> +#include <qwt_legend.h> +#include <QColor> +#include <cmath> +#include <iostream> +#include <volk/volk.h> +#include <gnuradio/math.h> +#include <boost/math/special_functions/round.hpp> + +class TimePrecisionClass +{ +public: + TimePrecisionClass(const int timePrecision) + { + _timePrecision = timePrecision; + } + + virtual ~TimePrecisionClass() + { + } + + virtual unsigned int getTimePrecision() const + { + return _timePrecision; + } + + virtual void setTimePrecision(const unsigned int newPrecision) + { + _timePrecision = newPrecision; + } +protected: + unsigned int _timePrecision; +}; + + +class HistogramDisplayZoomer: public QwtPlotZoomer, public TimePrecisionClass +{ +public: + HistogramDisplayZoomer(QwtPlotCanvas* canvas, const unsigned int timePrecision) + : QwtPlotZoomer(canvas),TimePrecisionClass(timePrecision) + { + setTrackerMode(QwtPicker::AlwaysOn); + } + + virtual ~HistogramDisplayZoomer() + { + } + + virtual void updateTrackerText() + { + updateDisplay(); + } + + void setUnitType(const std::string &type) + { + _unitType = type; + } + +protected: + using QwtPlotZoomer::trackerText; + virtual QwtText trackerText(const QPoint& p) const + { + QwtText t; + QwtDoublePoint dp = QwtPlotZoomer::invTransform(p); + if((dp.y() > 0.0001) && (dp.y() < 10000)) { + t.setText(QString("%1, %2"). + arg(dp.x(), 0, 'f', 4). + arg(dp.y(), 0, 'f', 0)); + } + else { + t.setText(QString("%1, %2"). + arg(dp.x(), 0, 'f', 4). + arg(dp.y(), 0, 'e', 0)); + } + + return t; + } + +private: + std::string _unitType; +}; + + +/*********************************************************************** + * Main Time domain plotter widget + **********************************************************************/ +HistogramDisplayPlot::HistogramDisplayPlot(int nplots, QWidget* parent) + : DisplayPlot(nplots, parent) +{ + _bins = 100; + _accum = false; + + // Initialize x-axis data array + _xAxisPoints = new double[_bins]; + memset(_xAxisPoints, 0x0, _bins*sizeof(double)); + + _zoomer = new HistogramDisplayZoomer(canvas(), 0); + +#if QWT_VERSION < 0x060000 + _zoomer->setSelectionFlags(QwtPicker::RectSelection | QwtPicker::DragSelection); +#endif + + _zoomer->setMousePattern(QwtEventPattern::MouseSelect2, + Qt::RightButton, Qt::ControlModifier); + _zoomer->setMousePattern(QwtEventPattern::MouseSelect3, + Qt::RightButton); + + const QColor c(Qt::darkRed); + _zoomer->setRubberBandPen(c); + _zoomer->setTrackerPen(c); + + _magnifier->setAxisEnabled(QwtPlot::xBottom, true); + _magnifier->setAxisEnabled(QwtPlot::yLeft, false); + + d_semilogx = false; + d_semilogy = false; + + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + setXaxis(-1, 1); + setAxisTitle(QwtPlot::xBottom, "Value"); + + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setYaxis(-2.0, _bins); + setAxisTitle(QwtPlot::yLeft, "Count"); + + QList<QColor> colors; + colors << QColor(Qt::blue) << QColor(Qt::red) << QColor(Qt::green) + << QColor(Qt::black) << QColor(Qt::cyan) << QColor(Qt::magenta) + << QColor(Qt::yellow) << QColor(Qt::gray) << QColor(Qt::darkRed) + << QColor(Qt::darkGreen) << QColor(Qt::darkBlue) << QColor(Qt::darkGray); + + // Setup dataPoints and plot vectors + // Automatically deleted when parent is deleted + for(int i = 0; i < _nplots; i++) { + _yDataPoints.push_back(new double[_bins]); + memset(_yDataPoints[i], 0, _bins*sizeof(double)); + + _plot_curve.push_back(new QwtPlotCurve(QString("Data %1").arg(i))); + _plot_curve[i]->attach(this); + _plot_curve[i]->setPen(QPen(colors[i])); + _plot_curve[i]->setRenderHint(QwtPlotItem::RenderAntialiased); + + // Adjust color's transparency for the brush + colors[i].setAlpha(127 / _nplots); + _plot_curve[i]->setBrush(QBrush(colors[i])); + + colors[i].setAlpha(255 / _nplots); + QwtSymbol *symbol = new QwtSymbol(QwtSymbol::NoSymbol, QBrush(colors[i]), + QPen(colors[i]), QSize(7,7)); + +#if QWT_VERSION < 0x060000 + _plot_curve[i]->setRawData(_xAxisPoints, _yDataPoints[i], _bins); + _plot_curve[i]->setSymbol(*symbol); +#else + _plot_curve[i]->setRawSamples(_xAxisPoints, _yDataPoints[i], _bins); + _plot_curve[i]->setSymbol(symbol); +#endif + } + + _resetXAxisPoints(-1, 1); +} + +HistogramDisplayPlot::~HistogramDisplayPlot() +{ + for(int i = 0; i < _nplots; i++) + delete[] _yDataPoints[i]; + delete[] _xAxisPoints; + + // _zoomer and _panner deleted when parent deleted +} + +void +HistogramDisplayPlot::replot() +{ + QwtPlot::replot(); +} + +void +HistogramDisplayPlot::plotNewData(const std::vector<double*> dataPoints, + const int64_t numDataPoints, + const double timeInterval) +{ + if(!_stop) { + if((numDataPoints > 0)) { + + double bottom = *std::min_element(dataPoints[0], dataPoints[0]+numDataPoints); + double top = *std::max_element(dataPoints[0], dataPoints[0]+numDataPoints); + for(int n = 1; n < _nplots; n++) { + bottom = std::min(bottom, *std::min_element(dataPoints[n], dataPoints[n]+numDataPoints)); + top = std::max(top, *std::max_element(dataPoints[n], dataPoints[n]+numDataPoints)); + } + + _resetXAxisPoints(bottom, top); + + int index; + double width = (top - bottom)/(_bins-1); + + // Something's wrong with the data (NaN, Inf, or something else) + if((bottom == top) || (bottom > top)) + return; + + for(int n = 0; n < _nplots; n++) { + if(!_accum) + memset(_yDataPoints[n], 0, _bins*sizeof(double)); + for(int64_t point = 0; point < numDataPoints; point++) { + index = boost::math::iround(1e-20 + (dataPoints[n][point] - bottom)/width); + index = std::max(std::min(index, _bins), 0); + _yDataPoints[n][static_cast<int>(index)] += 1; + } + } + + double height = *std::max_element(_yDataPoints[0], _yDataPoints[0]+_bins); + for(int n = 1; n < _nplots; n++) { + height = std::max(height, *std::max_element(_yDataPoints[n], _yDataPoints[n]+_bins)); + } + _autoScale(0, height); + + replot(); + } + } +} + +void +HistogramDisplayPlot::_resetXAxisPoints(double bottom, double top) +{ + double width = (top - bottom)/(_bins-1); + for(long loc = 0; loc < _bins; loc++){ + _xAxisPoints[loc] = bottom + loc*width; + } + + if(!_autoscale_state) { + bottom = axisScaleDiv(QwtPlot::xBottom)->lowerBound(); + top = axisScaleDiv(QwtPlot::xBottom)->upperBound(); + } + + // Set up zoomer base for maximum unzoom x-axis + // and reset to maximum unzoom level + QwtDoubleRect zbase = _zoomer->zoomBase(); + + if(d_semilogx) { + setAxisScale(QwtPlot::xBottom, 1e-1, top); + zbase.setLeft(1e-1); + } + else { + setAxisScale(QwtPlot::xBottom, bottom, top); + zbase.setLeft(bottom); + } + + zbase.setRight(top); + _zoomer->zoom(zbase); + _zoomer->setZoomBase(zbase); + _zoomer->zoom(0); +} + +void +HistogramDisplayPlot::_autoScale(double bottom, double top) +{ + // Auto scale the y-axis with a margin of 20% (10 dB for log scale) + double _bot = bottom - fabs(bottom)*0.20; + double _top = top + fabs(top)*0.20; + if(d_semilogy) { + if(bottom > 0) { + setYaxis(_bot-10, _top+10); + } + else { + setYaxis(1e-3, _top+10); + } + } + else { + setYaxis(_bot, _top); + } +} + +void +HistogramDisplayPlot::setAutoScale(bool state) +{ + _autoscale_state = state; +} + +void +HistogramDisplayPlot::setSemilogx(bool en) +{ + d_semilogx = en; + if(!d_semilogx) { + setAxisScaleEngine(QwtPlot::xBottom, new QwtLinearScaleEngine); + } + else { + setAxisScaleEngine(QwtPlot::xBottom, new QwtLog10ScaleEngine); + } +} + +void +HistogramDisplayPlot::setSemilogy(bool en) +{ + if(d_semilogy != en) { + d_semilogy = en; + double max = axisScaleDiv(QwtPlot::yLeft)->upperBound(); + if(!d_semilogy) { + setAxisScaleEngine(QwtPlot::yLeft, new QwtLinearScaleEngine); + setYaxis(-pow(10.0, max/10.0), pow(10.0, max/10.0)); + } + else { + setAxisScaleEngine(QwtPlot::yLeft, new QwtLog10ScaleEngine); + setYaxis(1e-10, 10.0*log10(100*max)); + } + } +} + +void +HistogramDisplayPlot::setAccumulate(bool state) +{ + _accum = state; +} + +void +HistogramDisplayPlot::setMarkerAlpha(int which, int alpha) +{ + if(which < _nplots) { + // Get the pen color + QPen pen(_plot_curve[which]->pen()); + QBrush brush(_plot_curve[which]->brush()); + QColor color = brush.color(); + + // Set new alpha and update pen + color.setAlpha(alpha); + brush.setColor(color); + color.setAlpha(std::min(255, static_cast<int>(alpha*1.5))); + pen.setColor(color); + _plot_curve[which]->setBrush(brush); + _plot_curve[which]->setPen(pen); + + // And set the new color for the markers +#if QWT_VERSION < 0x060000 + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + setLineMarker(which, sym.style()); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + if(sym) { + sym->setColor(color); + sym->setPen(pen); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +int +HistogramDisplayPlot::getMarkerAlpha(int which) const +{ + if(which < _nplots) { + return _plot_curve[which]->brush().color().alpha(); + } + else { + return 0; + } +} + +void +HistogramDisplayPlot::setLineColor(int which, QColor color) +{ + if(which < _nplots) { + // Adjust color's transparency for the brush + color.setAlpha(127 / _nplots); + + QBrush brush(_plot_curve[which]->brush()); + brush.setColor(color); + _plot_curve[which]->setBrush(brush); + + // Adjust color's transparency darker for the pen and markers + color.setAlpha(255 / _nplots); + + QPen pen(_plot_curve[which]->pen()); + pen.setColor(color); + _plot_curve[which]->setPen(pen); + +#if QWT_VERSION < 0x060000 + _plot_curve[which]->setPen(pen); + QwtSymbol sym = (QwtSymbol)_plot_curve[which]->symbol(); + setLineMarker(which, sym.style()); +#else + QwtSymbol *sym = (QwtSymbol*)_plot_curve[which]->symbol(); + if(sym) { + sym->setColor(color); + sym->setPen(pen); + _plot_curve[which]->setSymbol(sym); + } +#endif + } +} + +void +HistogramDisplayPlot::setNumBins(int bins) +{ + _bins = bins; + + delete [] _xAxisPoints; + _xAxisPoints = new double[_bins]; + memset(_xAxisPoints, 0x0, _bins*sizeof(double)); + + for(int i = 0; i < _nplots; i++) { + delete [] _yDataPoints[i]; + _yDataPoints[i] = new double[_bins]; + memset(_yDataPoints[i], 0, _bins*sizeof(double)); + +#if QWT_VERSION < 0x060000 + _plot_curve[i]->setRawData(_xAxisPoints, _yDataPoints[i], _bins); +#else + _plot_curve[i]->setRawSamples(_xAxisPoints, _yDataPoints[i], _bins); +#endif + } +} + +#endif /* HISTOGRAM_DISPLAY_PLOT_C */ diff --git a/gr-qtgui/lib/displayform.cc b/gr-qtgui/lib/displayform.cc index ecc878b0a8..ce83c96389 100644 --- a/gr-qtgui/lib/displayform.cc +++ b/gr-qtgui/lib/displayform.cc @@ -300,6 +300,11 @@ DisplayForm::markerAlpha(int which) } void +DisplayForm::setSampleRate(const QString &rate) +{ +} + +void DisplayForm::setStop(bool on) { if(!on) { diff --git a/gr-qtgui/lib/histogram_sink_f_impl.cc b/gr-qtgui/lib/histogram_sink_f_impl.cc new file mode 100644 index 0000000000..cd4012f4f5 --- /dev/null +++ b/gr-qtgui/lib/histogram_sink_f_impl.cc @@ -0,0 +1,391 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "histogram_sink_f_impl.h" +#include <gnuradio/io_signature.h> +#include <string.h> +#include <volk/volk.h> +#include <gnuradio/fft/fft.h> +#include <qwt_symbol.h> + +namespace gr { + namespace qtgui { + + histogram_sink_f::sptr + histogram_sink_f::make(int size, int bins, + double xmin, double xmax, + const std::string &name, + int nconnections, + QWidget *parent) + { + return gnuradio::get_initial_sptr + (new histogram_sink_f_impl(size, bins, xmin, xmax, name, + nconnections, parent)); + } + + histogram_sink_f_impl::histogram_sink_f_impl(int size, int bins, + double xmin, double xmax, + const std::string &name, + int nconnections, + QWidget *parent) + : sync_block("histogram_sink_f", + io_signature::make(nconnections, nconnections, sizeof(float)), + io_signature::make(0, 0, 0)), + d_size(size), d_bins(bins), d_xmin(xmin), d_xmax(xmax), d_name(name), + d_nconnections(nconnections), d_parent(parent) + { + d_main_gui = NULL; + + d_index = 0; + + for(int i = 0; i < d_nconnections; i++) { + d_residbufs.push_back(fft::malloc_double(d_size)); + memset(d_residbufs[i], 0, d_size*sizeof(double)); + } + + // Set alignment properties for VOLK + const int alignment_multiple = + volk_get_alignment() / sizeof(gr_complex); + set_alignment(std::max(1,alignment_multiple)); + + initialize(); + } + + histogram_sink_f_impl::~histogram_sink_f_impl() + { + if(!d_main_gui->isClosed()) + d_main_gui->close(); + + // d_main_gui is a qwidget destroyed with its parent + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + } + } + + bool + histogram_sink_f_impl::check_topology(int ninputs, int noutputs) + { + return ninputs == d_nconnections; + } + + void + histogram_sink_f_impl::initialize() + { + if(qApp != NULL) { + d_qApplication = qApp; + } + else { + int argc=0; + char **argv = NULL; + d_qApplication = new QApplication(argc, argv); + } + + d_main_gui = new HistogramDisplayForm(d_nconnections, d_parent); + d_main_gui->setNumBins(d_bins); + d_main_gui->setNPoints(d_size); + d_main_gui->setXaxis(d_xmin, d_xmax); + + // initialize update time to 10 times a second + set_update_time(0.1); + } + + void + histogram_sink_f_impl::exec_() + { + d_qApplication->exec(); + } + + QWidget* + histogram_sink_f_impl::qwidget() + { + return d_main_gui; + } + + PyObject* + histogram_sink_f_impl::pyqwidget() + { + PyObject *w = PyLong_FromVoidPtr((void*)d_main_gui); + PyObject *retarg = Py_BuildValue("N", w); + return retarg; + } + + void + histogram_sink_f_impl::set_y_axis(double min, double max) + { + d_main_gui->setYaxis(min, max); + } + + void + histogram_sink_f_impl::set_x_axis(double min, double max) + { + d_main_gui->setXaxis(min, max); + } + + void + histogram_sink_f_impl::set_update_time(double t) + { + //convert update time to ticks + gr::high_res_timer_type tps = gr::high_res_timer_tps(); + d_update_time = t * tps; + d_main_gui->setUpdateTime(t); + d_last_time = 0; + } + + void + histogram_sink_f_impl::set_title(const std::string &title) + { + d_main_gui->setTitle(title.c_str()); + } + + void + histogram_sink_f_impl::set_line_label(int which, const std::string &label) + { + d_main_gui->setLineLabel(which, label.c_str()); + } + + void + histogram_sink_f_impl::set_line_color(int which, const std::string &color) + { + d_main_gui->setLineColor(which, color.c_str()); + } + + void + histogram_sink_f_impl::set_line_width(int which, int width) + { + d_main_gui->setLineWidth(which, width); + } + + void + histogram_sink_f_impl::set_line_style(int which, int style) + { + d_main_gui->setLineStyle(which, (Qt::PenStyle)style); + } + + void + histogram_sink_f_impl::set_line_marker(int which, int marker) + { + d_main_gui->setLineMarker(which, (QwtSymbol::Style)marker); + } + + void + histogram_sink_f_impl::set_line_alpha(int which, double alpha) + { + d_main_gui->setMarkerAlpha(which, (int)(255.0*alpha)); + } + + void + histogram_sink_f_impl::set_size(int width, int height) + { + d_main_gui->resize(QSize(width, height)); + } + + std::string + histogram_sink_f_impl::title() + { + return d_main_gui->title().toStdString(); + } + + std::string + histogram_sink_f_impl::line_label(int which) + { + return d_main_gui->lineLabel(which).toStdString(); + } + + std::string + histogram_sink_f_impl::line_color(int which) + { + return d_main_gui->lineColor(which).toStdString(); + } + + int + histogram_sink_f_impl::line_width(int which) + { + return d_main_gui->lineWidth(which); + } + + int + histogram_sink_f_impl::line_style(int which) + { + return d_main_gui->lineStyle(which); + } + + int + histogram_sink_f_impl::line_marker(int which) + { + return d_main_gui->lineMarker(which); + } + + double + histogram_sink_f_impl::line_alpha(int which) + { + return (double)(d_main_gui->markerAlpha(which))/255.0; + } + + void + histogram_sink_f_impl::set_nsamps(const int newsize) + { + gr::thread::scoped_lock lock(d_mutex); + + if(newsize != d_size) { + // Resize residbuf and replace data + for(int i = 0; i < d_nconnections; i++) { + fft::free(d_residbufs[i]); + d_residbufs[i] = fft::malloc_double(newsize); + + memset(d_residbufs[i], 0, newsize*sizeof(double)); + } + + // Set new size and reset buffer index + // (throws away any currently held data, but who cares?) + d_size = newsize; + d_index = 0; + + d_main_gui->setNPoints(d_size); + } + } + + void + histogram_sink_f_impl::set_bins(const int bins) + { + gr::thread::scoped_lock lock(d_mutex); + d_bins = bins; + d_main_gui->setNumBins(d_bins); + } + + int + histogram_sink_f_impl::nsamps() const + { + return d_size; + } + + int + histogram_sink_f_impl::bins() const + { + return d_bins; + } + + void + histogram_sink_f_impl::npoints_resize() + { + int newsize = d_main_gui->getNPoints(); + set_nsamps(newsize); + } + + void + histogram_sink_f_impl::enable_menu(bool en) + { + d_main_gui->enableMenu(en); + } + + void + histogram_sink_f_impl::enable_grid(bool en) + { + d_main_gui->setGrid(en); + } + + void + histogram_sink_f_impl::enable_autoscale(bool en) + { + d_main_gui->autoScale(en); + } + + void + histogram_sink_f_impl::enable_semilogx(bool en) + { + d_main_gui->setSemilogx(en); + } + + void + histogram_sink_f_impl::enable_semilogy(bool en) + { + d_main_gui->setSemilogy(en); + } + + void + histogram_sink_f_impl::enable_accumulate(bool en) + { + d_main_gui->setAccumulate(en); + } + + void + histogram_sink_f_impl::reset() + { + d_index = 0; + } + + int + histogram_sink_f_impl::work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) + { + int n=0, j=0, idx=0; + const float *in = (const float*)input_items[idx]; + + npoints_resize(); + + for(int i=0; i < noutput_items; i+=d_size) { + unsigned int datasize = noutput_items - i; + unsigned int resid = d_size-d_index; + idx = 0; + + // If we have enough input for one full plot, do it + if(datasize >= resid) { + + // Fill up residbufs with d_size number of items + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + &in[j], resid); + } + + // Update the plot if its time + if(gr::high_res_timer_now() - d_last_time > d_update_time) { + d_last_time = gr::high_res_timer_now(); + d_qApplication->postEvent(d_main_gui, + new HistogramUpdateEvent(d_residbufs, d_size)); + } + + d_index = 0; + j += resid; + } + // Otherwise, copy what we received into the residbufs for next time + // because we set the output_multiple, this should never need to be called + else { + for(n = 0; n < d_nconnections; n++) { + in = (const float*)input_items[idx++]; + volk_32f_convert_64f_u(&d_residbufs[n][d_index], + &in[j], datasize); + } + d_index += datasize; + j += datasize; + } + } + + return j; + } + + } /* namespace qtgui */ +} /* namespace gr */ diff --git a/gr-qtgui/lib/histogram_sink_f_impl.h b/gr-qtgui/lib/histogram_sink_f_impl.h new file mode 100644 index 0000000000..83c8558c1b --- /dev/null +++ b/gr-qtgui/lib/histogram_sink_f_impl.h @@ -0,0 +1,113 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QTGUI_HISTOGRAM_SINK_F_IMPL_H +#define INCLUDED_QTGUI_HISTOGRAM_SINK_F_IMPL_H + +#include <gnuradio/qtgui/histogram_sink_f.h> +#include <gnuradio/qtgui/histogramdisplayform.h> +#include <gnuradio/thread/thread.h> +#include <gnuradio/high_res_timer.h> + +namespace gr { + namespace qtgui { + + class QTGUI_API histogram_sink_f_impl : public histogram_sink_f + { + private: + void initialize(); + + gr::thread::mutex d_mutex; + + int d_size; + int d_bins; + double d_xmin, d_xmax; + std::string d_name; + int d_nconnections; + + int d_index; + std::vector<double*> d_residbufs; + + QWidget *d_parent; + HistogramDisplayForm *d_main_gui; + + gr::high_res_timer_type d_update_time; + gr::high_res_timer_type d_last_time; + + void npoints_resize(); + + public: + histogram_sink_f_impl(int size, int bins, + double xmin, double xmax, + const std::string &name, + int nconnections, + QWidget *parent=NULL); + ~histogram_sink_f_impl(); + + bool check_topology(int ninputs, int noutputs); + + void exec_(); + QWidget* qwidget(); + PyObject* pyqwidget(); + + void set_y_axis(double min, double max); + void set_x_axis(double min, double max); + void set_update_time(double t); + void set_title(const std::string &title); + void set_line_label(int which, const std::string &label); + void set_line_color(int which, const std::string &color); + void set_line_width(int which, int width); + void set_line_style(int which, int style); + void set_line_marker(int which, int marker); + void set_line_alpha(int which, double alpha); + void set_nsamps(const int newsize); + void set_bins(const int bins); + + std::string title(); + std::string line_label(int which); + std::string line_color(int which); + int line_width(int which); + int line_style(int which); + int line_marker(int which); + double line_alpha(int which); + + void set_size(int width, int height); + + void enable_menu(bool en); + void enable_grid(bool en); + void enable_autoscale(bool en); + void enable_semilogx(bool en); + void enable_semilogy(bool en); + void enable_accumulate(bool en); + int nsamps() const; + int bins() const; + void reset(); + + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + }; + + } /* namespace qtgui */ +} /* namespace gr */ + +#endif /* INCLUDED_QTGUI_HISTOGRAM_SINK_F_IMPL_H */ diff --git a/gr-qtgui/lib/histogramdisplayform.cc b/gr-qtgui/lib/histogramdisplayform.cc new file mode 100644 index 0000000000..a56ad86d3a --- /dev/null +++ b/gr-qtgui/lib/histogramdisplayform.cc @@ -0,0 +1,174 @@ +/* -*- c++ -*- */ +/* + * Copyright 2013 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include <cmath> +#include <QMessageBox> +#include <gnuradio/qtgui/histogramdisplayform.h> +#include <iostream> + +HistogramDisplayForm::HistogramDisplayForm(int nplots, QWidget* parent) + : DisplayForm(nplots, parent) +{ + d_semilogx = false; + d_semilogy = false; + + _intValidator = new QIntValidator(this); + _intValidator->setBottom(0); + + _layout = new QGridLayout(this); + _displayPlot = new HistogramDisplayPlot(nplots, this); + _layout->addWidget(_displayPlot, 0, 0); + setLayout(_layout); + + NPointsMenu *nptsmenu = new NPointsMenu(this); + _menu->addAction(nptsmenu); + connect(nptsmenu, SIGNAL(whichTrigger(int)), + this, SLOT(setNPoints(const int))); + + NPointsMenu *nbinsmenu = new NPointsMenu(this); + nbinsmenu->setText("Number of Bins"); + _menu->addAction(nbinsmenu); + connect(nbinsmenu, SIGNAL(whichTrigger(int)), + this, SLOT(setNumBins(const int))); + + QAction *accummenu = new QAction("Accumulate", this); + accummenu->setCheckable(true); + _menu->addAction(accummenu); + connect(accummenu, SIGNAL(triggered(bool)), + this, SLOT(setAccumulate(bool))); + +// d_semilogxmenu = new QAction("Semilog X", this); +// d_semilogxmenu->setCheckable(true); +// _menu->addAction(d_semilogxmenu); +// connect(d_semilogxmenu, SIGNAL(triggered(bool)), +// this, SLOT(setSemilogx(bool))); +// +// d_semilogymenu = new QAction("Semilog Y", this); +// d_semilogymenu->setCheckable(true); +// _menu->addAction(d_semilogymenu); +// connect(d_semilogymenu, SIGNAL(triggered(bool)), +// this, SLOT(setSemilogy(bool))); + + Reset(); + + connect(_displayPlot, SIGNAL(plotPointSelected(const QPointF)), + this, SLOT(onPlotPointSelected(const QPointF))); +} + +HistogramDisplayForm::~HistogramDisplayForm() +{ + // Qt deletes children when parent is deleted + + // Don't worry about deleting Display Plots - they are deleted when parents are deleted + delete _intValidator; +} + +HistogramDisplayPlot* +HistogramDisplayForm::getPlot() +{ + return ((HistogramDisplayPlot*)_displayPlot); +} + +void +HistogramDisplayForm::newData(const QEvent* updateEvent) +{ + HistogramUpdateEvent *hevent = (HistogramUpdateEvent*)updateEvent; + const std::vector<double*> dataPoints = hevent->getDataPoints(); + const uint64_t numDataPoints = hevent->getNumDataPoints(); + + getPlot()->plotNewData(dataPoints, + numDataPoints, + d_update_time); +} + +void +HistogramDisplayForm::customEvent(QEvent * e) +{ + if(e->type() == HistogramUpdateEvent::Type()) { + newData(e); + } +} + +void +HistogramDisplayForm::setYaxis(double min, double max) +{ + getPlot()->setYaxis(min, max); +} + +void +HistogramDisplayForm::setXaxis(double min, double max) +{ + getPlot()->setXaxis(min, max); +} + +int +HistogramDisplayForm::getNPoints() const +{ + return d_npoints; +} + +void +HistogramDisplayForm::setNPoints(const int npoints) +{ + d_npoints = npoints; +} + +void +HistogramDisplayForm::autoScale(bool en) +{ + _autoscale_state = en; + _autoscale_act->setChecked(en); + getPlot()->setAutoScale(_autoscale_state); + getPlot()->replot(); +} + +void +HistogramDisplayForm::setSemilogx(bool en) +{ + d_semilogx = en; + d_semilogxmenu->setChecked(en); + getPlot()->setSemilogx(d_semilogx); + getPlot()->replot(); +} + +void +HistogramDisplayForm::setSemilogy(bool en) +{ + d_semilogy = en; + d_semilogymenu->setChecked(en); + getPlot()->setSemilogy(d_semilogy); + getPlot()->replot(); +} + +void +HistogramDisplayForm::setNumBins(const int bins) +{ + getPlot()->setNumBins(bins); + getPlot()->replot(); +} + +void +HistogramDisplayForm::setAccumulate(bool en) +{ + getPlot()->setAccumulate(en); + getPlot()->replot(); +} diff --git a/gr-qtgui/lib/spectrumUpdateEvents.cc b/gr-qtgui/lib/spectrumUpdateEvents.cc index 7d7f689f25..393cd3edec 100644 --- a/gr-qtgui/lib/spectrumUpdateEvents.cc +++ b/gr-qtgui/lib/spectrumUpdateEvents.cc @@ -439,4 +439,47 @@ TimeRasterUpdateEvent::getNumDataPoints() const return _numDataPoints; } +/***************************************************************************/ + + +HistogramUpdateEvent::HistogramUpdateEvent(const std::vector<double*> points, + const uint64_t npoints) + : QEvent(QEvent::Type(SpectrumUpdateEventType)) +{ + if(npoints < 1) { + _npoints = 1; + } + else { + _npoints = npoints; + } + + _nplots = points.size(); + for(size_t i = 0; i < _nplots; i++) { + _points.push_back(new double[_npoints]); + if(npoints > 0) { + memcpy(_points[i], points[i], _npoints*sizeof(double)); + } + } +} + +HistogramUpdateEvent::~HistogramUpdateEvent() +{ + for(size_t i = 0; i < _nplots; i++) { + delete[] _points[i]; + } +} + +const std::vector<double*> +HistogramUpdateEvent::getDataPoints() const +{ + return _points; +} + +uint64_t +HistogramUpdateEvent::getNumDataPoints() const +{ + return _npoints; +} + + #endif /* SPECTRUM_UPDATE_EVENTS_C */ diff --git a/gr-qtgui/python/qtgui/qa_qtgui.py b/gr-qtgui/python/qtgui/qa_qtgui.py index 51c0727de4..d98cf1fc28 100755 --- a/gr-qtgui/python/qtgui/qa_qtgui.py +++ b/gr-qtgui/python/qtgui/qa_qtgui.py @@ -77,5 +77,8 @@ class test_qtgui(gr_unittest.TestCase): self.qtsnk = qtgui.time_raster_sink_f(1024, 100, 100.5, [], [], "Test", 1) + def test12(self): + self.qtsnk = qtgui.histogram_sink_f(1024, 100, -1, 1, "Test", 1) + if __name__ == '__main__': gr_unittest.run(test_qtgui, "test_qtgui.xml") diff --git a/gr-qtgui/swig/qtgui_swig.i b/gr-qtgui/swig/qtgui_swig.i index f974fa2e86..124c5ee7fc 100644 --- a/gr-qtgui/swig/qtgui_swig.i +++ b/gr-qtgui/swig/qtgui_swig.i @@ -46,6 +46,7 @@ #include "gnuradio/qtgui/const_sink_c.h" #include "gnuradio/qtgui/waterfall_sink_c.h" #include "gnuradio/qtgui/waterfall_sink_f.h" +#include "gnuradio/qtgui/histogram_sink_f.h" %} %include "gnuradio/qtgui/sink_c.h" @@ -59,6 +60,7 @@ %include "gnuradio/qtgui/const_sink_c.h" %include "gnuradio/qtgui/waterfall_sink_c.h" %include "gnuradio/qtgui/waterfall_sink_f.h" +%include "gnuradio/qtgui/histogram_sink_f.h" GR_SWIG_BLOCK_MAGIC2(qtgui, sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, sink_f); @@ -71,3 +73,4 @@ GR_SWIG_BLOCK_MAGIC2(qtgui, freq_sink_f); GR_SWIG_BLOCK_MAGIC2(qtgui, const_sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, waterfall_sink_c); GR_SWIG_BLOCK_MAGIC2(qtgui, waterfall_sink_f); +GR_SWIG_BLOCK_MAGIC2(qtgui, histogram_sink_f); diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py index b7efa07623..02a89afc56 100644 --- a/gr-uhd/grc/gen_uhd_usrp_blocks.py +++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py @@ -36,7 +36,11 @@ MAIN_TMPL = """\ \#if \$stream_args() args=\$stream_args, \#end if + \#if \$stream_chans() + channels=\$stream_chans, + \#else channels=range(\$nchan), + \#end if ), ) \#if \$clock_rate() @@ -146,6 +150,19 @@ self.\$(id).set_bandwidth(\$bw$(n), $n) </option> </param> <param> + <name>Stream channels</name> + <key>stream_chans</key> + <value>[]</value> + <type>int_vector</type> + <hide> + \#if \$stream_chans() + none + \#else + part + \#end if + </hide> + </param> + <param> <name>Device Addr</name> <key>dev_addr</key> <value></value> diff --git a/gr-uhd/lib/gr_uhd_common.h b/gr-uhd/lib/gr_uhd_common.h index 0c18fa4634..f6a0f91cad 100644 --- a/gr-uhd/lib/gr_uhd_common.h +++ b/gr-uhd/lib/gr_uhd_common.h @@ -49,4 +49,19 @@ namespace gr { } /* namespace uhd */ } /* namespace gr */ +/*! + * The stream args ensure function sanitizes random user input. + * We may extend this to handle more things in the future, + * but ATM it ensures that the channels are initialized. + */ +static inline uhd::stream_args_t stream_args_ensure(const uhd::stream_args_t &args) +{ + uhd::stream_args_t sanitized = args; + if (sanitized.channels.empty()) + { + sanitized.channels.push_back(0); + } + return sanitized; +} + #endif /* INCLUDED_GR_UHD_COMMON_H */ diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index d38ffc344d..828537fab3 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -55,7 +55,7 @@ namespace gr { { check_abi(); return usrp_sink::sptr - (new usrp_sink_impl(device_addr, stream_args)); + (new usrp_sink_impl(device_addr, stream_args_ensure(stream_args))); } usrp_sink_impl::usrp_sink_impl(const ::uhd::device_addr_t &device_addr, @@ -64,7 +64,7 @@ namespace gr { args_to_io_sig(stream_args), io_signature::make(0, 0, 0)), _stream_args(stream_args), - _nchan(std::max<size_t>(1, stream_args.channels.size())), + _nchan(stream_args.channels.size()), _stream_now(_nchan == 1), _start_time_set(false) { @@ -82,6 +82,7 @@ namespace gr { ::uhd::dict<std::string, std::string> usrp_sink_impl::get_usrp_info(size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_GET_USRP_INFO_API return _dev->get_usrp_tx_info(chan); #else @@ -105,21 +106,24 @@ namespace gr { void usrp_sink_impl::set_samp_rate(double rate) { - _dev->set_tx_rate(rate); + BOOST_FOREACH(const size_t chan, _stream_args.channels) + { + _dev->set_tx_rate(rate, chan); + } _sample_rate = this->get_samp_rate(); } double usrp_sink_impl::get_samp_rate(void) { - return _dev->get_tx_rate(); + return _dev->get_tx_rate(_stream_args.channels[0]); } ::uhd::meta_range_t usrp_sink_impl::get_samp_rates(void) { #ifdef UHD_USRP_MULTI_USRP_GET_RATES_API - return _dev->get_tx_rates(); + return _dev->get_tx_rates(_stream_args.channels[0]); #else throw std::runtime_error("not implemented in this version"); #endif @@ -129,24 +133,28 @@ namespace gr { usrp_sink_impl::set_center_freq(const ::uhd::tune_request_t tune_request, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_tx_freq(tune_request, chan); } double usrp_sink_impl::get_center_freq(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_freq(chan); } ::uhd::freq_range_t usrp_sink_impl::get_freq_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_freq_range(chan); } void usrp_sink_impl::set_gain(double gain, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_tx_gain(gain, chan); } @@ -155,30 +163,35 @@ namespace gr { const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_tx_gain(gain, name, chan); } double usrp_sink_impl::get_gain(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_gain(chan); } double usrp_sink_impl::get_gain(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_gain(name, chan); } std::vector<std::string> usrp_sink_impl::get_gain_names(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_gain_names(chan); } ::uhd::gain_range_t usrp_sink_impl::get_gain_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_gain_range(chan); } @@ -186,6 +199,7 @@ namespace gr { usrp_sink_impl::get_gain_range(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_gain_range(name, chan); } @@ -193,36 +207,42 @@ namespace gr { usrp_sink_impl::set_antenna(const std::string &ant, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_tx_antenna(ant, chan); } std::string usrp_sink_impl::get_antenna(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_antenna(chan); } std::vector<std::string> usrp_sink_impl::get_antennas(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_antennas(chan); } void usrp_sink_impl::set_bandwidth(double bandwidth, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_tx_bandwidth(bandwidth, chan); } double usrp_sink_impl::get_bandwidth(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_bandwidth(chan); } ::uhd::freq_range_t usrp_sink_impl::get_bandwidth_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_bandwidth_range(chan); } @@ -230,6 +250,7 @@ namespace gr { usrp_sink_impl::set_dc_offset(const std::complex<double> &offset, size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API return _dev->set_tx_dc_offset(offset, chan); #else @@ -241,6 +262,7 @@ namespace gr { usrp_sink_impl::set_iq_balance(const std::complex<double> &correction, size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API return _dev->set_tx_iq_balance(correction, chan); #else @@ -251,12 +273,14 @@ namespace gr { ::uhd::sensor_value_t usrp_sink_impl::get_sensor(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_sensor(name, chan); } std::vector<std::string> usrp_sink_impl::get_sensor_names(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_sensor_names(chan); } @@ -409,6 +433,7 @@ namespace gr { ::uhd::usrp::dboard_iface::sptr usrp_sink_impl::get_dboard_iface(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_tx_dboard_iface(chan); } diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc index c5df60222a..318bdb627d 100644 --- a/gr-uhd/lib/usrp_source_impl.cc +++ b/gr-uhd/lib/usrp_source_impl.cc @@ -58,7 +58,7 @@ namespace gr { { check_abi(); return usrp_source::sptr - (new usrp_source_impl(device_addr, stream_args)); + (new usrp_source_impl(device_addr, stream_args_ensure(stream_args))); } usrp_source_impl::usrp_source_impl(const ::uhd::device_addr_t &device_addr, @@ -67,7 +67,7 @@ namespace gr { io_signature::make(0, 0, 0), args_to_io_sig(stream_args)), _stream_args(stream_args), - _nchan(std::max<size_t>(1, stream_args.channels.size())), + _nchan(stream_args.channels.size()), _stream_now(_nchan == 1), _tag_now(false), _start_time_set(false) @@ -89,6 +89,7 @@ namespace gr { ::uhd::dict<std::string, std::string> usrp_source_impl::get_usrp_info(size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_GET_USRP_INFO_API return _dev->get_usrp_rx_info(chan); #else @@ -111,22 +112,25 @@ namespace gr { void usrp_source_impl::set_samp_rate(double rate) { - _dev->set_rx_rate(rate); + BOOST_FOREACH(const size_t chan, _stream_args.channels) + { + _dev->set_rx_rate(rate, chan); + } _samp_rate = this->get_samp_rate(); _tag_now = true; } - double + double usrp_source_impl::get_samp_rate(void) { - return _dev->get_rx_rate(); + return _dev->get_rx_rate(_stream_args.channels[0]); } ::uhd::meta_range_t usrp_source_impl::get_samp_rates(void) { #ifdef UHD_USRP_MULTI_USRP_GET_RATES_API - return _dev->get_rx_rates(); + return _dev->get_rx_rates(_stream_args.channels[0]); #else throw std::runtime_error("not implemented in this version"); #endif @@ -136,8 +140,10 @@ namespace gr { usrp_source_impl::set_center_freq(const ::uhd::tune_request_t tune_request, size_t chan) { + const size_t user_chan = chan; + chan = _stream_args.channels[chan]; const ::uhd::tune_result_t res = _dev->set_rx_freq(tune_request, chan); - _center_freq = this->get_center_freq(chan); + _center_freq = this->get_center_freq(user_chan); _tag_now = true; return res; } @@ -145,96 +151,112 @@ namespace gr { double usrp_source_impl::get_center_freq(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_freq(chan); } ::uhd::freq_range_t usrp_source_impl::get_freq_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_freq_range(chan); } void usrp_source_impl::set_gain(double gain, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_rx_gain(gain, chan); } void usrp_source_impl::set_gain(double gain, const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_rx_gain(gain, name, chan); } double usrp_source_impl::get_gain(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_gain(chan); } double usrp_source_impl::get_gain(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_gain(name, chan); } std::vector<std::string> usrp_source_impl::get_gain_names(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_gain_names(chan); } ::uhd::gain_range_t usrp_source_impl::get_gain_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_gain_range(chan); } ::uhd::gain_range_t usrp_source_impl::get_gain_range(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_gain_range(name, chan); } void usrp_source_impl::set_antenna(const std::string &ant, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_rx_antenna(ant, chan); } std::string usrp_source_impl::get_antenna(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_antenna(chan); } std::vector<std::string> usrp_source_impl::get_antennas(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_antennas(chan); } void usrp_source_impl::set_bandwidth(double bandwidth, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->set_rx_bandwidth(bandwidth, chan); } double usrp_source_impl::get_bandwidth(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_bandwidth(chan); } ::uhd::freq_range_t usrp_source_impl::get_bandwidth_range(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_bandwidth_range(chan); } void usrp_source_impl::set_auto_dc_offset(const bool enable, size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API return _dev->set_rx_dc_offset(enable, chan); #else @@ -246,6 +268,7 @@ namespace gr { usrp_source_impl::set_dc_offset(const std::complex<double> &offset, size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API return _dev->set_rx_dc_offset(offset, chan); #else @@ -257,6 +280,7 @@ namespace gr { usrp_source_impl::set_iq_balance(const std::complex<double> &correction, size_t chan) { + chan = _stream_args.channels[chan]; #ifdef UHD_USRP_MULTI_USRP_FRONTEND_CAL_API return _dev->set_rx_iq_balance(correction, chan); #else @@ -267,12 +291,14 @@ namespace gr { ::uhd::sensor_value_t usrp_source_impl::get_sensor(const std::string &name, size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_sensor(name, chan); } std::vector<std::string> usrp_source_impl::get_sensor_names(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_sensor_names(chan); } @@ -423,6 +449,7 @@ namespace gr { ::uhd::usrp::dboard_iface::sptr usrp_source_impl::get_dboard_iface(size_t chan) { + chan = _stream_args.channels[chan]; return _dev->get_rx_dboard_iface(chan); } @@ -455,7 +482,10 @@ namespace gr { void usrp_source_impl::issue_stream_cmd(const ::uhd::stream_cmd_t &cmd) { - _dev->issue_stream_cmd(cmd); + for (size_t i = 0; i < _stream_args.channels.size(); i++) + { + _dev->issue_stream_cmd(cmd, _stream_args.channels[i]); + } } bool @@ -476,7 +506,7 @@ namespace gr { else { stream_cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); } - _dev->issue_stream_cmd(stream_cmd); + this->issue_stream_cmd(stream_cmd); _tag_now = true; return true; } @@ -507,7 +537,7 @@ namespace gr { bool usrp_source_impl::stop(void) { - _dev->issue_stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + this->issue_stream_cmd(::uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); this->flush(); @@ -551,7 +581,7 @@ namespace gr { cmd.stream_now = _stream_now; static const double reasonable_delay = 0.1; //order of magnitude over RTT cmd.time_spec = get_time_now() + ::uhd::time_spec_t(reasonable_delay); - _dev->issue_stream_cmd(cmd); + this->issue_stream_cmd(cmd); //receive samples until timeout const size_t actual_num_samps = _rx_stream->recv diff --git a/gr-utils/python/modtool/gr-newmod/cmake/Modules/FindGnuradioRuntime.cmake b/gr-utils/python/modtool/gr-newmod/cmake/Modules/FindGnuradioRuntime.cmake index 2833fb1781..afed684a5e 100644 --- a/gr-utils/python/modtool/gr-newmod/cmake/Modules/FindGnuradioRuntime.cmake +++ b/gr-utils/python/modtool/gr-newmod/cmake/Modules/FindGnuradioRuntime.cmake @@ -31,5 +31,6 @@ if(PC_GNURADIO_RUNTIME_FOUND) endif(PC_GNURADIO_RUNTIME_FOUND) INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS) +# do not check GNURADIO_RUNTIME_INCLUDE_DIRS, is not set when default include path us used. +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_RUNTIME DEFAULT_MSG GNURADIO_RUNTIME_LIBRARIES) MARK_AS_ADVANCED(GNURADIO_RUNTIME_LIBRARIES GNURADIO_RUNTIME_INCLUDE_DIRS) diff --git a/gr-utils/python/modtool/modtool_base.py b/gr-utils/python/modtool/modtool_base.py index a03334bedc..081c10aaf9 100644 --- a/gr-utils/python/modtool/modtool_base.py +++ b/gr-utils/python/modtool/modtool_base.py @@ -131,7 +131,8 @@ class ModTool(object): return False for f in files: if os.path.isfile(f) and f == 'CMakeLists.txt': - if re.search('find_package\(GnuradioRuntime\)', open(f).read()) is not None: + if re.search('find_package\(GnuradioRuntime\)', open(f).read()) is not None or \ + re.search('find_package\(Gnuradio(\s+[0-9".]+)?\)', open(f).read()) is not None: self._info['version'] = '36' # Might be 37, check that later has_makefile = True elif re.search('GR_REGISTER_COMPONENT', open(f).read()) is not None: diff --git a/gr-utils/python/modtool/templates.py b/gr-utils/python/modtool/templates.py index 227a8d3971..47314891c0 100644 --- a/gr-utils/python/modtool/templates.py +++ b/gr-utils/python/modtool/templates.py @@ -228,8 +228,16 @@ namespace gr { gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { +#if $blocktype == 'source' +#silent pass +#else const <+ITYPE+> *in = (const <+ITYPE+> *) input_items[0]; +#end if +#if $blocktype == 'sink' +#silent pass +#else <+OTYPE+> *out = (<+OTYPE+> *) output_items[0]; +#end if // Do <+signal processing+> diff --git a/grc/base/Block.py b/grc/base/Block.py index 223f3183b7..9736c0ac44 100644 --- a/grc/base/Block.py +++ b/grc/base/Block.py @@ -22,256 +22,397 @@ from Element import Element from Cheetah.Template import Template from UserDict import UserDict +from .. gui import Actions class TemplateArg(UserDict): - """ - A cheetah template argument created from a param. - The str of this class evaluates to the param's to code method. - The use of this class as a dictionary (enum only) will reveal the enum opts. - The __call__ or () method can return the param evaluated to a raw python data type. - """ - - def __init__(self, param): - UserDict.__init__(self) - self._param = param - if param.is_enum(): - for key in param.get_opt_keys(): - self[key] = str(param.get_opt(key)) - - def __str__(self): - return str(self._param.to_code()) - - def __call__(self): - return self._param.get_evaluated() + """ + A cheetah template argument created from a param. + The str of this class evaluates to the param's to code method. + The use of this class as a dictionary (enum only) will reveal the enum opts. + The __call__ or () method can return the param evaluated to a raw python data type. + """ + + def __init__(self, param): + UserDict.__init__(self) + self._param = param + if param.is_enum(): + for key in param.get_opt_keys(): + self[key] = str(param.get_opt(key)) + + def __str__(self): + return str(self._param.to_code()) + + def __call__(self): + return self._param.get_evaluated() def _get_keys(lst): return [elem.get_key() for elem in lst] def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Block(Element): - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #build the block - Element.__init__(self, flow_graph) - #grab the data - params = n.findall('param') - sources = n.findall('source') - sinks = n.findall('sink') - self._name = n.find('name') - self._key = n.find('key') - self._category = n.find('category') or '' - self._grc_source = n.find('grc_source') or '' - self._block_wrapper_path = n.find('block_wrapper_path') - #create the param objects - self._params = list() - #add the id param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'ID', - 'key': 'id', - 'type': 'id', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'Enabled', - 'key': '_enabled', - 'type': 'raw', - 'value': 'True', - 'hide': 'all', - }) - )) - for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): - key = param.get_key() - #test against repeated keys - if key in self.get_param_keys(): - raise Exception, 'Key "%s" already exists in params'%key - #store the param - self.get_params().append(param) - #create the source objects - self._sources = list() - for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): - key = source.get_key() - #test against repeated keys - if key in self.get_source_keys(): - raise Exception, 'Key "%s" already exists in sources'%key - #store the port - self.get_sources().append(source) - #create the sink objects - self._sinks = list() - for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): - key = sink.get_key() - #test against repeated keys - if key in self.get_sink_keys(): - raise Exception, 'Key "%s" already exists in sinks'%key - #store the port - self.get_sinks().append(sink) - - def get_enabled(self): - """ - Get the enabled state of the block. - - Returns: - true for enabled - """ - try: return eval(self.get_param('_enabled').get_value()) - except: return True - - def set_enabled(self, enabled): - """ - Set the enabled state of the block. - - Args: - enabled: true for enabled - """ - self.get_param('_enabled').set_value(str(enabled)) - - def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) - - def get_id(self): return self.get_param('id').get_value() - def is_block(self): return True - def get_name(self): return self._name - def get_key(self): return self._key - def get_category(self): return self._category - def get_doc(self): return '' - def get_ports(self): return self.get_sources() + self.get_sinks() - def get_children(self): return self.get_ports() + self.get_params() - def get_block_wrapper_path(self): return self._block_wrapper_path - - ############################################## - # Access Params - ############################################## - def get_param_keys(self): return _get_keys(self._params) - def get_param(self, key): return _get_elem(self._params, key) - def get_params(self): return self._params - - ############################################## - # Access Sinks - ############################################## - def get_sink_keys(self): return _get_keys(self._sinks) - def get_sink(self, key): return _get_elem(self._sinks, key) - def get_sinks(self): return self._sinks - - ############################################## - # Access Sources - ############################################## - def get_source_keys(self): return _get_keys(self._sources) - def get_source(self, key): return _get_elem(self._sources, key) - def get_sources(self): return self._sources - - def get_connections(self): - return sum([port.get_connections() for port in self.get_ports()], []) - - def resolve_dependencies(self, tmpl): - """ - Resolve a paramater dependency with cheetah templates. - - Args: - tmpl: the string with dependencies - - Returns: - the resolved value - """ - tmpl = str(tmpl) - if '$' not in tmpl: return tmpl - n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) - try: return str(Template(tmpl, n)) - except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl) - - ############################################## - # Controller Modify - ############################################## - def type_controller_modify(self, direction): - """ - Change the type controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - type_param = None - for param in filter(lambda p: p.is_enum(), self.get_params()): - children = self.get_ports() + self.get_params() - #priority to the type controller - if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param - #use param if type param is unset - if not type_param: type_param = param - if type_param: - #try to increment the enum by direction - try: - keys = type_param.get_option_keys() - old_index = keys.index(type_param.get_value()) - new_index = (old_index + direction + len(keys))%len(keys) - type_param.set_value(keys[new_index]) - changed = True - except: pass - return changed - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - return False - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this block's params to nested data. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['param'] = map(lambda p: p.export_data(), self.get_params()) - return n - - def import_data(self, n): - """ - Import this block's params from nested data. - Any param keys that do not exist will be ignored. - Since params can be dynamically created based another param, - call rewrite, and repeat the load until the params stick. - This call to rewrite will also create any dynamic ports - that are needed for the connections creation phase. - - Args: - n: the nested data odict - """ - get_hash = lambda: hash(tuple(map(hash, self.get_params()))) - my_hash = 0 - while get_hash() != my_hash: - params_n = n.findall('param') - for param_n in params_n: - key = param_n.find('key') - value = param_n.find('value') - #the key must exist in this block's params - if key in self.get_param_keys(): - self.get_param(key).set_value(value) - #store hash and call rewrite - my_hash = get_hash() - self.rewrite() + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + + Args: + flow: graph the parent element + n: the nested odict + + Returns: + block a new block + """ + #build the block + Element.__init__(self, flow_graph) + #grab the data + params = n.findall('param') + sources = n.findall('source') + sinks = n.findall('sink') + self._name = n.find('name') + self._key = n.find('key') + self._category = n.find('category') or '' + self._grc_source = n.find('grc_source') or '' + self._block_wrapper_path = n.find('block_wrapper_path') + self._bussify_sink = n.find('bus_sink') + self._bussify_source = n.find('bus_source') + #create the param objects + self._params = list() + #add the id param + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'ID', + 'key': 'id', + 'type': 'id', + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'Enabled', + 'key': '_enabled', + 'type': 'raw', + 'value': 'True', + 'hide': 'all', + }) + )) + for param in map(lambda n: self.get_parent().get_parent().Param(block=self, n=n), params): + key = param.get_key() + #test against repeated keys + if key in self.get_param_keys(): + raise Exception, 'Key "%s" already exists in params'%key + #store the param + self.get_params().append(param) + #create the source objects + self._sources = list() + for source in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='source'), sources): + key = source.get_key() + #test against repeated keys + if key in self.get_source_keys(): + raise Exception, 'Key "%s" already exists in sources'%key + #store the port + self.get_sources().append(source) + self.back_ofthe_bus(self.get_sources()) + #create the sink objects + self._sinks = list() + for sink in map(lambda n: self.get_parent().get_parent().Port(block=self, n=n, dir='sink'), sinks): + key = sink.get_key() + #test against repeated keys + if key in self.get_sink_keys(): + raise Exception, 'Key "%s" already exists in sinks'%key + #store the port + self.get_sinks().append(sink) + self.back_ofthe_bus(self.get_sinks()) + self.current_bus_structure = {'source':'','sink':''}; + + # Virtual source/sink and pad source/sink blocks are + # indistinguishable from normal GR blocks. Make explicit + # checks for them here since they have no work function or + # buffers to manage. + is_not_virtual_or_pad = ((self._key != "virtual_source") \ + and (self._key != "virtual_sink") \ + and (self._key != "pad_source") \ + and (self._key != "pad_sink")) + + if (len(sources) or len(sinks)) and is_not_virtual_or_pad: + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Core Affinity', + 'key': 'affinity', + 'type': 'int_vector', + 'hide': 'part', + }) + )) + if len(sources) and is_not_virtual_or_pad: + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({'name': 'Min Output Buffer', + 'key': 'minoutbuf', + 'type': 'int', + 'hide': 'part', + 'value': '0' + }) + )) + + def back_ofthe_bus(self, portlist): + portlist.sort(key=lambda a: a.get_type() == 'bus'); + + + def filter_bus_port(self, ports): + buslist = [i for i in ports if i.get_type() == 'bus']; + if len(buslist) == 0: + return ports; + else: + return buslist; + + def get_enabled(self): + """ + Get the enabled state of the block. + + Returns: + true for enabled + """ + try: return eval(self.get_param('_enabled').get_value()) + except: return True + + def set_enabled(self, enabled): + """ + Set the enabled state of the block. + + Args: + enabled: true for enabled + """ + self.get_param('_enabled').set_value(str(enabled)) + + def __str__(self): return 'Block - %s - %s(%s)'%(self.get_id(), self.get_name(), self.get_key()) + + def get_id(self): return self.get_param('id').get_value() + def is_block(self): return True + def get_name(self): return self._name + def get_key(self): return self._key + def get_category(self): return self._category + def get_doc(self): return '' + def get_ports(self): return self.get_sources() + self.get_sinks() + def get_ports_gui(self): return self.filter_bus_port(self.get_sources()) + self.filter_bus_port(self.get_sinks()); + def get_children(self): return self.get_ports() + self.get_params() + def get_children_gui(self): return self.get_ports_gui() + self.get_params() + def get_block_wrapper_path(self): return self._block_wrapper_path + + ############################################## + # Access Params + ############################################## + def get_param_keys(self): return _get_keys(self._params) + def get_param(self, key): return _get_elem(self._params, key) + def get_params(self): return self._params + def has_param(self, key): + try: + _get_elem(self._params, key); + return True; + except: + return False; + + ############################################## + # Access Sinks + ############################################## + def get_sink_keys(self): return _get_keys(self._sinks) + def get_sink(self, key): return _get_elem(self._sinks, key) + def get_sinks(self): return self._sinks + def get_sinks_gui(self): return self.filter_bus_port(self.get_sinks()) + + ############################################## + # Access Sources + ############################################## + def get_source_keys(self): return _get_keys(self._sources) + def get_source(self, key): return _get_elem(self._sources, key) + def get_sources(self): return self._sources + def get_sources_gui(self): return self.filter_bus_port(self.get_sources()); + + def get_connections(self): + return sum([port.get_connections() for port in self.get_ports()], []) + + def resolve_dependencies(self, tmpl): + """ + Resolve a paramater dependency with cheetah templates. + + Args: + tmpl: the string with dependencies + + Returns: + the resolved value + """ + tmpl = str(tmpl) + if '$' not in tmpl: return tmpl + n = dict((p.get_key(), TemplateArg(p)) for p in self.get_params()) + try: return str(Template(tmpl, n)) + except Exception, e: return "-------->\n%s: %s\n<--------"%(e, tmpl) + + ############################################## + # Controller Modify + ############################################## + def type_controller_modify(self, direction): + """ + Change the type controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + type_param = None + for param in filter(lambda p: p.is_enum(), self.get_params()): + children = self.get_ports() + self.get_params() + #priority to the type controller + if param.get_key() in ' '.join(map(lambda p: p._type, children)): type_param = param + #use param if type param is unset + if not type_param: type_param = param + if type_param: + #try to increment the enum by direction + try: + keys = type_param.get_option_keys() + old_index = keys.index(type_param.get_value()) + new_index = (old_index + direction + len(keys))%len(keys) + type_param.set_value(keys[new_index]) + changed = True + except: pass + return changed + + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + return False + + def form_bus_structure(self, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + struct = [range(len(get_p()))]; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + + + structlet = []; + last = 0; + for j in [i.get_nports() for i in get_p() if isinstance(i.get_nports(), int)]: + structlet.extend(map(lambda a: a+last, range(j))); + last = structlet[-1] + 1; + struct = [structlet]; + if bus_structure: + + struct = bus_structure + + self.current_bus_structure[direc] = struct; + return struct + + def bussify(self, n, direc): + if direc == 'source': + get_p = self.get_sources; + get_p_gui = self.get_sources_gui; + bus_structure = self.get_bus_structure('source'); + else: + get_p = self.get_sinks; + get_p_gui = self.get_sinks_gui + bus_structure = self.get_bus_structure('sink'); + + + for elt in get_p(): + for connect in elt.get_connections(): + self.get_parent().remove_element(connect); + + + + + + + if (not 'bus' in map(lambda a: a.get_type(), get_p())) and len(get_p()) > 0: + + struct = self.form_bus_structure(direc); + self.current_bus_structure[direc] = struct; + if get_p()[0].get_nports(): + n['nports'] = str(1); + + for i in range(len(struct)): + n['key'] = str(len(get_p())); + n = odict(n); + port = self.get_parent().get_parent().Port(block=self, n=n, dir=direc); + get_p().append(port); + + + + + elif 'bus' in map(lambda a: a.get_type(), get_p()): + for elt in get_p_gui(): + get_p().remove(elt); + self.current_bus_structure[direc] = '' + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this block's params to nested data. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['param'] = map(lambda p: p.export_data(), self.get_params()) + if 'bus' in map(lambda a: a.get_type(), self.get_sinks()): + n['bus_sink'] = str(1); + if 'bus' in map(lambda a: a.get_type(), self.get_sources()): + n['bus_source'] = str(1); + return n + + def import_data(self, n): + """ + Import this block's params from nested data. + Any param keys that do not exist will be ignored. + Since params can be dynamically created based another param, + call rewrite, and repeat the load until the params stick. + This call to rewrite will also create any dynamic ports + that are needed for the connections creation phase. + + Args: + n: the nested data odict + """ + get_hash = lambda: hash(tuple(map(hash, self.get_params()))) + my_hash = 0 + while get_hash() != my_hash: + params_n = n.findall('param') + for param_n in params_n: + key = param_n.find('key') + value = param_n.find('value') + #the key must exist in this block's params + if key in self.get_param_keys(): + self.get_param(key).set_value(value) + #store hash and call rewrite + my_hash = get_hash() + self.rewrite() + bussinks = n.findall('bus_sink'); + if len(bussinks) > 0 and not self._bussify_sink: + self.bussify({'name':'bus','type':'bus'}, 'sink') + elif len(bussinks) > 0: + self.bussify({'name':'bus','type':'bus'}, 'sink') + self.bussify({'name':'bus','type':'bus'}, 'sink') + bussrcs = n.findall('bus_source'); + if len(bussrcs) > 0 and not self._bussify_source: + self.bussify({'name':'bus','type':'bus'}, 'source') + elif len(bussrcs) > 0: + self.bussify({'name':'bus','type':'bus'}, 'source') + self.bussify({'name':'bus','type':'bus'}, 'source') + + diff --git a/grc/base/Connection.py b/grc/base/Connection.py index b9afe1434d..252b7deac1 100644 --- a/grc/base/Connection.py +++ b/grc/base/Connection.py @@ -22,84 +22,99 @@ from . import odict class Connection(Element): - def __init__(self, flow_graph, porta, portb): - """ - Make a new connection given the parent and 2 ports. - - Args: - flow_graph: the parent of this element - porta: a port (any direction) - portb: a port (any direction) - @throws Error cannot make connection - - Returns: - a new connection - """ - Element.__init__(self, flow_graph) - source = sink = None - #separate the source and sink - for port in (porta, portb): - if port.is_source(): source = port - if port.is_sink(): sink = port - if not source: raise ValueError('Connection could not isolate source') - if not sink: raise ValueError('Connection could not isolate sink') - #ensure that this connection (source -> sink) is unique - for connection in self.get_parent().get_connections(): - if connection.get_source() is source and connection.get_sink() is sink: - raise Exception('This connection between source and sink is not unique.') - self._source = source - self._sink = sink + def __init__(self, flow_graph, porta, portb): + """ + Make a new connection given the parent and 2 ports. + + Args: + flow_graph: the parent of this element + porta: a port (any direction) + portb: a port (any direction) + @throws Error cannot make connection + + Returns: + a new connection + """ + Element.__init__(self, flow_graph) + source = sink = None + #separate the source and sink + for port in (porta, portb): + if port.is_source(): source = port + if port.is_sink(): sink = port + if not source: raise ValueError('Connection could not isolate source') + if not sink: raise ValueError('Connection could not isolate sink') + busses = len(filter(lambda a: a.get_type() == 'bus', [source, sink]))%2 + if not busses == 0: raise ValueError('busses must get with busses') - def __str__(self): - return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( - self.get_source().get_parent(), - self.get_source(), - self.get_sink().get_parent(), - self.get_sink(), - ) + if not len(source.get_associated_ports()) == len(sink.get_associated_ports()): + raise ValueError('port connections must have same cardinality'); + #ensure that this connection (source -> sink) is unique + for connection in self.get_parent().get_connections(): + if connection.get_source() is source and connection.get_sink() is sink: + raise Exception('This connection between source and sink is not unique.') + self._source = source + self._sink = sink + if source.get_type() == 'bus': + + sources = source.get_associated_ports(); + sinks = sink.get_associated_ports(); + + for i in range(len(sources)): + try: + flow_graph.connect(sources[i], sinks[i]); + except: + pass - def is_connection(self): return True + def __str__(self): + return 'Connection (\n\t%s\n\t\t%s\n\t%s\n\t\t%s\n)'%( + self.get_source().get_parent(), + self.get_source(), + self.get_sink().get_parent(), + self.get_sink(), + ) - def validate(self): - """ - Validate the connections. - The ports must match in type. - """ - Element.validate(self) - source_type = self.get_source().get_type() - sink_type = self.get_sink().get_type() - if source_type != sink_type: - self.add_error_message('Source type "%s" does not match sink type "%s".'%(source_type, sink_type)) + def is_connection(self): return True - def get_enabled(self): - """ - Get the enabled state of this connection. - - Returns: - true if source and sink blocks are enabled - """ - return self.get_source().get_parent().get_enabled() and \ - self.get_sink().get_parent().get_enabled() + def validate(self): + """ + Validate the connections. + The ports must match in type. + """ + Element.validate(self) + source_type = self.get_source().get_type() + sink_type = self.get_sink().get_type() + if source_type != sink_type: + self.add_error_message('Source type "%s" does not match sink type "%s".'%(source_type, sink_type)) - ############################# - # Access Ports - ############################# - def get_sink(self): return self._sink - def get_source(self): return self._source + def get_enabled(self): + """ + Get the enabled state of this connection. + + Returns: + true if source and sink blocks are enabled + """ + return self.get_source().get_parent().get_enabled() and \ + self.get_sink().get_parent().get_enabled() - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this connection's info. - - Returns: - a nested data odict - """ - n = odict() - n['source_block_id'] = self.get_source().get_parent().get_id() - n['sink_block_id'] = self.get_sink().get_parent().get_id() - n['source_key'] = self.get_source().get_key() - n['sink_key'] = self.get_sink().get_key() - return n + ############################# + # Access Ports + ############################# + def get_sink(self): return self._sink + def get_source(self): return self._source + + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this connection's info. + + Returns: + a nested data odict + """ + n = odict() + n['source_block_id'] = self.get_source().get_parent().get_id() + n['sink_block_id'] = self.get_sink().get_parent().get_id() + n['source_key'] = self.get_source().get_key() + n['sink_key'] = self.get_sink().get_key() + return n diff --git a/grc/base/Element.py b/grc/base/Element.py index 74097eea9a..17b2234a8c 100644 --- a/grc/base/Element.py +++ b/grc/base/Element.py @@ -19,77 +19,77 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA class Element(object): - def __init__(self, parent=None): - self._parent = parent + def __init__(self, parent=None): + self._parent = parent - ################################################## - # Element Validation API - ################################################## - def validate(self): - """ - Validate this element and call validate on all children. - Call this base method before adding error messages in the subclass. - """ - self._error_messages = list() - for child in self.get_children(): child.validate() + ################################################## + # Element Validation API + ################################################## + def validate(self): + """ + Validate this element and call validate on all children. + Call this base method before adding error messages in the subclass. + """ + self._error_messages = list() + for child in self.get_children(): child.validate() - def is_valid(self): - """ - Is this element valid? - - Returns: - true when the element is enabled and has no error messages - """ - return not self.get_error_messages() or not self.get_enabled() + def is_valid(self): + """ + Is this element valid? + + Returns: + true when the element is enabled and has no error messages + """ + return not self.get_error_messages() or not self.get_enabled() - def add_error_message(self, msg): - """ - Add an error message to the list of errors. - - Args: - msg: the error message string - """ - self._error_messages.append(msg) + def add_error_message(self, msg): + """ + Add an error message to the list of errors. + + Args: + msg: the error message string + """ + self._error_messages.append(msg) - def get_error_messages(self): - """ - Get the list of error messages from this element and all of its children. - Do not include the error messages from disabled children. - Cleverly indent the children error messages for printing purposes. - - Returns: - a list of error message strings - """ - error_messages = list(self._error_messages) #make a copy - for child in filter(lambda c: c.get_enabled(), self.get_children()): - for msg in child.get_error_messages(): - error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t"))) - return error_messages + def get_error_messages(self): + """ + Get the list of error messages from this element and all of its children. + Do not include the error messages from disabled children. + Cleverly indent the children error messages for printing purposes. + + Returns: + a list of error message strings + """ + error_messages = list(self._error_messages) #make a copy + for child in filter(lambda c: c.get_enabled(), self.get_children()): + for msg in child.get_error_messages(): + error_messages.append("%s:\n\t%s"%(child, msg.replace("\n", "\n\t"))) + return error_messages - def rewrite(self): - """ - Rewrite this element and call rewrite on all children. - Call this base method before rewriting the element. - """ - for child in self.get_children(): child.rewrite() + def rewrite(self): + """ + Rewrite this element and call rewrite on all children. + Call this base method before rewriting the element. + """ + for child in self.get_children(): child.rewrite() - def get_enabled(self): return True + def get_enabled(self): return True - ############################################## - ## Tree-like API - ############################################## - def get_parent(self): return self._parent - def get_children(self): return list() + ############################################## + ## Tree-like API + ############################################## + def get_parent(self): return self._parent + def get_children(self): return list() - ############################################## - ## Type testing methods - ############################################## - def is_element(self): return True - def is_platform(self): return False - def is_flow_graph(self): return False - def is_connection(self): return False - def is_block(self): return False - def is_source(self): return False - def is_sink(self): return False - def is_port(self): return False - def is_param(self): return False + ############################################## + ## Type testing methods + ############################################## + def is_element(self): return True + def is_platform(self): return False + def is_flow_graph(self): return False + def is_connection(self): return False + def is_block(self): return False + def is_source(self): return False + def is_sink(self): return False + def is_port(self): return False + def is_param(self): return False diff --git a/grc/base/FlowGraph.py b/grc/base/FlowGraph.py index e8c49466d0..2f2d8c65e1 100644 --- a/grc/base/FlowGraph.py +++ b/grc/base/FlowGraph.py @@ -23,229 +23,288 @@ from .. gui import Messages class FlowGraph(Element): - def __init__(self, platform): - """ - Make a flow graph from the arguments. - - Args: - platform: a platforms with blocks and contrcutors - - Returns: - the flow graph object - """ - #initialize - Element.__init__(self, platform) - #inital blank import - self.import_data() - - def _get_unique_id(self, base_id=''): - """ - Get a unique id starting with the base id. - - Args: - base_id: the id starts with this and appends a count - - Returns: - a unique id - """ - index = 0 - while True: - id = '%s_%d'%(base_id, index) - index = index + 1 - #make sure that the id is not used by another block - if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id - - def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id')) - - def get_option(self, key): - """ - Get the option for a given key. - The option comes from the special options block. - - Args: - key: the param key for the options block - - Returns: - the value held by that param - """ - return self._options_block.get_param(key).get_evaluated() - - def is_flow_graph(self): return True - - ############################################## - ## Access Elements - ############################################## - def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] - def get_blocks(self): return filter(lambda e: e.is_block(), self.get_elements()) - def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) - def get_children(self): return self.get_elements() - def get_elements(self): - """ - Get a list of all the elements. - Always ensure that the options block is in the list (only once). - - Returns: - the element list - """ - options_block_count = self._elements.count(self._options_block) - if not options_block_count: - self._elements.append(self._options_block) - for i in range(options_block_count-1): - self._elements.remove(self._options_block) - return self._elements - - def get_enabled_blocks(self): - """ - Get a list of all blocks that are enabled. - - Returns: - a list of blocks - """ - return filter(lambda b: b.get_enabled(), self.get_blocks()) - - def get_enabled_connections(self): - """ - Get a list of all connections that are enabled. - - Returns: - a list of connections - """ - return filter(lambda c: c.get_enabled(), self.get_connections()) - - def get_new_block(self, key): - """ - Get a new block of the specified key. - Add the block to the list of elements. - - Args: - key: the block key - - Returns: - the new block or None if not found - """ - if key not in self.get_parent().get_block_keys(): return None - block = self.get_parent().get_new_block(self, key) - self.get_elements().append(block) - return block - - def connect(self, porta, portb): - """ - Create a connection between porta and portb. - - Args: - porta: a port - portb: another port - @throw Exception bad connection - - Returns: - the new connection - """ - connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) - self.get_elements().append(connection) - return connection - - def remove_element(self, element): - """ - Remove the element from the list of elements. - If the element is a port, remove the whole block. - If the element is a block, remove its connections. - If the element is a connection, just remove the connection. - """ - if element not in self.get_elements(): return - #found a port, set to parent signal block - if element.is_port(): - element = element.get_parent() - #remove block, remove all involved connections - if element.is_block(): - for port in element.get_ports(): - map(self.remove_element, port.get_connections()) - self.get_elements().remove(element) - - def evaluate(self, expr): - """ - Evaluate the expression. - - Args: - expr: the string expression - @throw NotImplementedError - """ - raise NotImplementedError - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this flow graph to nested data. - Export all block and connection data. - - Returns: - a nested data odict - """ - import time - n = odict() - n['timestamp'] = time.ctime() - n['block'] = [block.export_data() for block in self.get_blocks()] - n['connection'] = [connection.export_data() for connection in self.get_connections()] - return odict({'flow_graph': n}) - - def import_data(self, n=None): - """ - Import blocks and connections into this flow graph. - Clear this flowgraph of all previous blocks and connections. - Any blocks or connections in error will be ignored. - - Args: - n: the nested data odict - """ - #remove previous elements - self._elements = list() - #use blank data if none provided - fg_n = n and n.find('flow_graph') or odict() - blocks_n = fg_n.findall('block') - connections_n = fg_n.findall('connection') - #create option block - self._options_block = self.get_parent().get_new_block(self, 'options') - #build the blocks - for block_n in blocks_n: - key = block_n.find('key') - if key == 'options': block = self._options_block - else: block = self.get_new_block(key) - #only load the block when the block key was valid - if block: block.import_data(block_n) - else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent())) - #build the connections - for connection_n in connections_n: - #try to make the connection - try: - #get the block ids - source_block_id = connection_n.find('source_block_id') - sink_block_id = connection_n.find('sink_block_id') - #get the port keys - source_key = connection_n.find('source_key') - sink_key = connection_n.find('sink_key') - #verify the blocks - block_ids = map(lambda b: b.get_id(), self.get_blocks()) - if source_block_id not in block_ids: - raise LookupError('source block id "%s" not in block ids'%source_block_id) - if sink_block_id not in block_ids: - raise LookupError('sink block id "%s" not in block ids'%sink_block_id) - #get the blocks - source_block = self.get_block(source_block_id) - sink_block = self.get_block(sink_block_id) - #verify the ports - if source_key not in source_block.get_source_keys(): - raise LookupError('source key "%s" not in source block keys'%source_key) - if sink_key not in sink_block.get_sink_keys(): - raise LookupError('sink key "%s" not in sink block keys'%sink_key) - #get the ports - source = source_block.get_source(source_key) - sink = sink_block.get_sink(sink_key) - #build the connection - self.connect(source, sink) - except LookupError, e: Messages.send_error_load( - 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s'%( - source_block_id, source_key, sink_block_id, sink_key, e - ) - ) - self.rewrite() #global rewrite + def __init__(self, platform): + """ + Make a flow graph from the arguments. + + Args: + platform: a platforms with blocks and contrcutors + + Returns: + the flow graph object + """ + #initialize + Element.__init__(self, platform) + #inital blank import + self.import_data() + + def _get_unique_id(self, base_id=''): + """ + Get a unique id starting with the base id. + + Args: + base_id: the id starts with this and appends a count + + Returns: + a unique id + """ + index = 0 + while True: + id = '%s_%d'%(base_id, index) + index = index + 1 + #make sure that the id is not used by another block + if not filter(lambda b: b.get_id() == id, self.get_blocks()): return id + + def __str__(self): return 'FlowGraph - %s(%s)'%(self.get_option('title'), self.get_option('id')) + def rewrite(self): + def refactor_bus_structure(): + + for block in self.get_blocks(): + for direc in ['source', 'sink']: + if direc == 'source': + get_p = block.get_sources; + get_p_gui = block.get_sources_gui; + bus_structure = block.form_bus_structure('source'); + else: + get_p = block.get_sinks; + get_p_gui = block.get_sinks_gui + bus_structure = block.form_bus_structure('sink'); + + if 'bus' in map(lambda a: a.get_type(), get_p_gui()): + + + + if len(get_p_gui()) > len(bus_structure): + times = range(len(bus_structure), len(get_p_gui())); + for i in times: + for connect in get_p_gui()[-1].get_connections(): + block.get_parent().remove_element(connect); + get_p().remove(get_p_gui()[-1]); + elif len(get_p_gui()) < len(bus_structure): + n = {'name':'bus','type':'bus'}; + if True in map(lambda a: isinstance(a.get_nports(), int), get_p()): + n['nports'] = str(1); + + times = range(len(get_p_gui()), len(bus_structure)); + + for i in times: + n['key'] = str(len(get_p())); + n = odict(n); + port = block.get_parent().get_parent().Port(block=block, n=n, dir=direc); + get_p().append(port); + + + + for child in self.get_children(): child.rewrite() + refactor_bus_structure(); + + def get_option(self, key): + """ + Get the option for a given key. + The option comes from the special options block. + + Args: + key: the param key for the options block + + Returns: + the value held by that param + """ + return self._options_block.get_param(key).get_evaluated() + + def is_flow_graph(self): return True + + ############################################## + ## Access Elements + ############################################## + def get_block(self, id): return filter(lambda b: b.get_id() == id, self.get_blocks())[0] + def get_blocks_unordered(self): return filter(lambda e: e.is_block(), self.get_elements()) + def get_blocks(self): + blocks = self.get_blocks_unordered(); + for i in range(len(blocks)): + if blocks[i].get_key() == 'variable': + blk = blocks[i]; + blocks.remove(blk); + blocks.insert(1, blk); + return blocks; + def get_connections(self): return filter(lambda e: e.is_connection(), self.get_elements()) + def get_children(self): return self.get_elements() + def get_elements(self): + """ + Get a list of all the elements. + Always ensure that the options block is in the list (only once). + + Returns: + the element list + """ + options_block_count = self._elements.count(self._options_block) + if not options_block_count: + self._elements.append(self._options_block) + for i in range(options_block_count-1): + self._elements.remove(self._options_block) + return self._elements + + def get_enabled_blocks(self): + """ + Get a list of all blocks that are enabled. + + Returns: + a list of blocks + """ + return filter(lambda b: b.get_enabled(), self.get_blocks()) + + def get_enabled_connections(self): + """ + Get a list of all connections that are enabled. + + Returns: + a list of connections + """ + return filter(lambda c: c.get_enabled(), self.get_connections()) + + def get_new_block(self, key): + """ + Get a new block of the specified key. + Add the block to the list of elements. + + Args: + key: the block key + + Returns: + the new block or None if not found + """ + if key not in self.get_parent().get_block_keys(): return None + block = self.get_parent().get_new_block(self, key) + self.get_elements().append(block); + if block._bussify_sink: + block.bussify({'name':'bus','type':'bus'}, 'sink') + if block._bussify_source: + block.bussify({'name':'bus','type':'bus'}, 'source') + return block; + + def connect(self, porta, portb): + """ + Create a connection between porta and portb. + + Args: + porta: a port + portb: another port + @throw Exception bad connection + + Returns: + the new connection + """ + connection = self.get_parent().Connection(flow_graph=self, porta=porta, portb=portb) + self.get_elements().append(connection) + return connection + + def remove_element(self, element): + """ + Remove the element from the list of elements. + If the element is a port, remove the whole block. + If the element is a block, remove its connections. + If the element is a connection, just remove the connection. + """ + if element not in self.get_elements(): return + #found a port, set to parent signal block + if element.is_port(): + element = element.get_parent() + #remove block, remove all involved connections + if element.is_block(): + for port in element.get_ports(): + map(self.remove_element, port.get_connections()) + if element.is_connection(): + if element.is_bus(): + cons_list = [] + for i in map(lambda a: a.get_connections(), element.get_source().get_associated_ports()): + cons_list.extend(i); + map(self.remove_element, cons_list); + self.get_elements().remove(element) + + def evaluate(self, expr): + """ + Evaluate the expression. + + Args: + expr: the string expression + @throw NotImplementedError + """ + raise NotImplementedError + + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this flow graph to nested data. + Export all block and connection data. + + Returns: + a nested data odict + """ + import time + n = odict() + n['timestamp'] = time.ctime() + n['block'] = [block.export_data() for block in self.get_blocks()] + n['connection'] = [connection.export_data() for connection in self.get_connections()] + return odict({'flow_graph': n}) + + def import_data(self, n=None): + """ + Import blocks and connections into this flow graph. + Clear this flowgraph of all previous blocks and connections. + Any blocks or connections in error will be ignored. + + Args: + n: the nested data odict + """ + #remove previous elements + self._elements = list() + #use blank data if none provided + fg_n = n and n.find('flow_graph') or odict() + blocks_n = fg_n.findall('block') + connections_n = fg_n.findall('connection') + #create option block + self._options_block = self.get_parent().get_new_block(self, 'options') + #build the blocks + for block_n in blocks_n: + key = block_n.find('key') + if key == 'options': block = self._options_block + else: block = self.get_new_block(key) + #only load the block when the block key was valid + if block: block.import_data(block_n) + else: Messages.send_error_load('Block key "%s" not found in %s'%(key, self.get_parent())) + #build the connections + for connection_n in connections_n: + #try to make the connection + try: + #get the block ids + source_block_id = connection_n.find('source_block_id') + sink_block_id = connection_n.find('sink_block_id') + #get the port keys + source_key = connection_n.find('source_key') + sink_key = connection_n.find('sink_key') + #verify the blocks + block_ids = map(lambda b: b.get_id(), self.get_blocks()) + if source_block_id not in block_ids: + raise LookupError('source block id "%s" not in block ids'%source_block_id) + if sink_block_id not in block_ids: + raise LookupError('sink block id "%s" not in block ids'%sink_block_id) + #get the blocks + source_block = self.get_block(source_block_id) + sink_block = self.get_block(sink_block_id) + #verify the ports + if source_key not in source_block.get_source_keys(): + raise LookupError('source key "%s" not in source block keys'%source_key) + if sink_key not in sink_block.get_sink_keys(): + raise LookupError('sink key "%s" not in sink block keys'%sink_key) + #get the ports + source = source_block.get_source(source_key) + sink = sink_block.get_sink(sink_key) + #build the connection + self.connect(source, sink) + except LookupError, e: Messages.send_error_load( + 'Connection between %s(%s) and %s(%s) could not be made.\n\t%s'%( + source_block_id, source_key, sink_block_id, sink_key, e + ) + ) + self.rewrite() #global rewrite diff --git a/grc/base/Param.py b/grc/base/Param.py index 76a74d3ed7..8b8362ac1a 100644 --- a/grc/base/Param.py +++ b/grc/base/Param.py @@ -22,166 +22,166 @@ from Element import Element def _get_keys(lst): return [elem.get_key() for elem in lst] def _get_elem(lst, key): - try: return lst[_get_keys(lst).index(key)] - except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) + try: return lst[_get_keys(lst).index(key)] + except ValueError: raise ValueError, 'Key "%s" not found in %s.'%(key, _get_keys(lst)) class Option(Element): - def __init__(self, param, n): - Element.__init__(self, param) - self._name = n.find('name') - self._key = n.find('key') - self._opts = dict() - opts = n.findall('opt') - #test against opts when non enum - if not self.get_parent().is_enum() and opts: - raise Exception, 'Options for non-enum types cannot have sub-options' - #extract opts - for opt in opts: - #separate the key:value - try: key, value = opt.split(':') - except: raise Exception, 'Error separating "%s" into key:value'%opt - #test against repeated keys - if self._opts.has_key(key): - raise Exception, 'Key "%s" already exists in option'%key - #store the option - self._opts[key] = value - - def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key()) - def get_name(self): return self._name - def get_key(self): return self._key - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self._opts.keys() - def get_opt(self, key): return self._opts[key] - def get_opts(self): return self._opts.values() + def __init__(self, param, n): + Element.__init__(self, param) + self._name = n.find('name') + self._key = n.find('key') + self._opts = dict() + opts = n.findall('opt') + #test against opts when non enum + if not self.get_parent().is_enum() and opts: + raise Exception, 'Options for non-enum types cannot have sub-options' + #extract opts + for opt in opts: + #separate the key:value + try: key, value = opt.split(':') + except: raise Exception, 'Error separating "%s" into key:value'%opt + #test against repeated keys + if self._opts.has_key(key): + raise Exception, 'Key "%s" already exists in option'%key + #store the option + self._opts[key] = value + + def __str__(self): return 'Option %s(%s)'%(self.get_name(), self.get_key()) + def get_name(self): return self._name + def get_key(self): return self._key + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): return self._opts.keys() + def get_opt(self, key): return self._opts[key] + def get_opts(self): return self._opts.values() class Param(Element): - def __init__(self, block, n): - """ - Make a new param from nested data. - - Args: - block: the parent element - n: the nested odict - """ - #grab the data - self._name = n.find('name') - self._key = n.find('key') - value = n.find('value') or '' - self._type = n.find('type') - self._hide = n.find('hide') or '' - #build the param - Element.__init__(self, block) - #create the Option objects from the n data - self._options = list() - for option in map(lambda o: Option(param=self, n=o), n.findall('option')): - key = option.get_key() - #test against repeated keys - if key in self.get_option_keys(): - raise Exception, 'Key "%s" already exists in options'%key - #store the option - self.get_options().append(option) - #test the enum options - if self.is_enum(): - #test against options with identical keys - if len(set(self.get_option_keys())) != len(self.get_options()): - raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() - #test against inconsistent keys in options - opt_keys = self.get_options()[0].get_opt_keys() - for option in self.get_options(): - if set(opt_keys) != set(option.get_opt_keys()): - raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys - #if a value is specified, it must be in the options keys - self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0] - if self.get_value() not in self.get_option_keys(): - raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) - else: self._value = value or '' - - def validate(self): - """ - Validate the param. - The value must be evaluated and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) - - def get_evaluated(self): raise NotImplementedError - - def to_code(self): - """ - Convert the value to code. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_types(self): - """ - Get a list of all possible param types. - @throw NotImplementedError - """ - raise NotImplementedError - - def get_color(self): return '#FFFFFF' - def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) - def is_param(self): return True - def get_name(self): return self._name - def get_key(self): return self._key - def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() - - def get_value(self): - value = self._value - if self.is_enum() and value not in self.get_option_keys(): - value = self.get_option_keys()[0] - self.set_value(value) - return value - - def set_value(self, value): self._value = str(value) #must be a string - - def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def is_enum(self): return self._type == 'enum' - - def __repr__(self): - """ - Get the repr (nice string format) for this param. - Just return the value (special case enum). - Derived classes can handle complex formatting. - - Returns: - the string representation - """ - if self.is_enum(): return self.get_option(self.get_value()).get_name() - return self.get_value() - - ############################################## - # Access Options - ############################################## - def get_option_keys(self): return _get_keys(self.get_options()) - def get_option(self, key): return _get_elem(self.get_options(), key) - def get_options(self): return self._options - - ############################################## - # Access Opts - ############################################## - def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() - def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) - def get_opts(self): return self.get_option(self.get_value()).get_opts() - - ############################################## - ## Import/Export Methods - ############################################## - def export_data(self): - """ - Export this param's key/value. - - Returns: - a nested data odict - """ - n = odict() - n['key'] = self.get_key() - n['value'] = self.get_value() - return n + def __init__(self, block, n): + """ + Make a new param from nested data. + + Args: + block: the parent element + n: the nested odict + """ + #grab the data + self._name = n.find('name') + self._key = n.find('key') + value = n.find('value') or '' + self._type = n.find('type') + self._hide = n.find('hide') or '' + #build the param + Element.__init__(self, block) + #create the Option objects from the n data + self._options = list() + for option in map(lambda o: Option(param=self, n=o), n.findall('option')): + key = option.get_key() + #test against repeated keys + if key in self.get_option_keys(): + raise Exception, 'Key "%s" already exists in options'%key + #store the option + self.get_options().append(option) + #test the enum options + if self.is_enum(): + #test against options with identical keys + if len(set(self.get_option_keys())) != len(self.get_options()): + raise Exception, 'Options keys "%s" are not unique.'%self.get_option_keys() + #test against inconsistent keys in options + opt_keys = self.get_options()[0].get_opt_keys() + for option in self.get_options(): + if set(opt_keys) != set(option.get_opt_keys()): + raise Exception, 'Opt keys "%s" are not identical across all options.'%opt_keys + #if a value is specified, it must be in the options keys + self._value = value if value or value in self.get_option_keys() else self.get_option_keys()[0] + if self.get_value() not in self.get_option_keys(): + raise Exception, 'The value "%s" is not in the possible values of "%s".'%(self.get_value(), self.get_option_keys()) + else: self._value = value or '' + + def validate(self): + """ + Validate the param. + The value must be evaluated and type must a possible type. + """ + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) + + def get_evaluated(self): raise NotImplementedError + + def to_code(self): + """ + Convert the value to code. + @throw NotImplementedError + """ + raise NotImplementedError + + def get_types(self): + """ + Get a list of all possible param types. + @throw NotImplementedError + """ + raise NotImplementedError + + def get_color(self): return '#FFFFFF' + def __str__(self): return 'Param - %s(%s)'%(self.get_name(), self.get_key()) + def is_param(self): return True + def get_name(self): return self._name + def get_key(self): return self._key + def get_hide(self): return self.get_parent().resolve_dependencies(self._hide).strip() + + def get_value(self): + value = self._value + if self.is_enum() and value not in self.get_option_keys(): + value = self.get_option_keys()[0] + self.set_value(value) + return value + + def set_value(self, value): self._value = str(value) #must be a string + + def get_type(self): return self.get_parent().resolve_dependencies(self._type) + def is_enum(self): return self._type == 'enum' + + def __repr__(self): + """ + Get the repr (nice string format) for this param. + Just return the value (special case enum). + Derived classes can handle complex formatting. + + Returns: + the string representation + """ + if self.is_enum(): return self.get_option(self.get_value()).get_name() + return self.get_value() + + ############################################## + # Access Options + ############################################## + def get_option_keys(self): return _get_keys(self.get_options()) + def get_option(self, key): return _get_elem(self.get_options(), key) + def get_options(self): return self._options + + ############################################## + # Access Opts + ############################################## + def get_opt_keys(self): return self.get_option(self.get_value()).get_opt_keys() + def get_opt(self, key): return self.get_option(self.get_value()).get_opt(key) + def get_opts(self): return self.get_option(self.get_value()).get_opts() + + ############################################## + ## Import/Export Methods + ############################################## + def export_data(self): + """ + Export this param's key/value. + + Returns: + a nested data odict + """ + n = odict() + n['key'] = self.get_key() + n['value'] = self.get_value() + return n diff --git a/grc/base/ParseXML.py b/grc/base/ParseXML.py index 0d19f6b212..56097395dd 100644 --- a/grc/base/ParseXML.py +++ b/grc/base/ParseXML.py @@ -21,98 +21,99 @@ from lxml import etree from . import odict class XMLSyntaxError(Exception): - def __init__(self, error_log): - self._error_log = error_log - def __str__(self): - return '\n'.join(map(str, self._error_log.filter_from_errors())) + def __init__(self, error_log): + self._error_log = error_log + def __str__(self): + return '\n'.join(map(str, self._error_log.filter_from_errors())) def validate_dtd(xml_file, dtd_file=None): - """ - Validate an xml file against its dtd. - - Args: - xml_file: the xml file - dtd_file: the optional dtd file - @throws Exception validation fails - """ - #perform parsing, use dtd validation if dtd file is not specified - parser = etree.XMLParser(dtd_validation=not dtd_file) - xml = etree.parse(xml_file, parser=parser) - if parser.error_log: raise XMLSyntaxError(parser.error_log) - #perform dtd validation if the dtd file is specified - if not dtd_file: return - dtd = etree.DTD(dtd_file) - if not dtd.validate(xml.getroot()): raise XMLSyntaxError(dtd.error_log) + """ + Validate an xml file against its dtd. + + Args: + xml_file: the xml file + dtd_file: the optional dtd file + @throws Exception validation fails + """ + #perform parsing, use dtd validation if dtd file is not specified + parser = etree.XMLParser(dtd_validation=not dtd_file) + xml = etree.parse(xml_file, parser=parser) + if parser.error_log: raise XMLSyntaxError(parser.error_log) + #perform dtd validation if the dtd file is specified + if not dtd_file: return + dtd = etree.DTD(dtd_file) + if not dtd.validate(xml.getroot()): raise XMLSyntaxError(dtd.error_log) def from_file(xml_file): - """ - Create nested data from an xml file using the from xml helper. - - Args: - xml_file: the xml file path - - Returns: - the nested data - """ - xml = etree.parse(xml_file).getroot() - return _from_file(xml) + """ + Create nested data from an xml file using the from xml helper. + + Args: + xml_file: the xml file path + + Returns: + the nested data + """ + xml = etree.parse(xml_file).getroot() + return _from_file(xml) def _from_file(xml): - """ - Recursivly parse the xml tree into nested data format. - - Args: - xml: the xml tree - - Returns: - the nested data - """ - tag = xml.tag - if not len(xml): - return odict({tag: xml.text or ''}) #store empty tags (text is None) as empty string - nested_data = odict() - for elem in xml: - key, value = _from_file(elem).items()[0] - if nested_data.has_key(key): nested_data[key].append(value) - else: nested_data[key] = [value] - #delistify if the length of values is 1 - for key, values in nested_data.iteritems(): - if len(values) == 1: nested_data[key] = values[0] - return odict({tag: nested_data}) + """ + Recursivly parse the xml tree into nested data format. + + Args: + xml: the xml tree + + Returns: + the nested data + """ + tag = xml.tag + if not len(xml): + return odict({tag: xml.text or ''}) #store empty tags (text is None) as empty string + nested_data = odict() + for elem in xml: + key, value = _from_file(elem).items()[0] + if nested_data.has_key(key): nested_data[key].append(value) + else: nested_data[key] = [value] + #delistify if the length of values is 1 + for key, values in nested_data.iteritems(): + if len(values) == 1: nested_data[key] = values[0] + + return odict({tag: nested_data}) def to_file(nested_data, xml_file): - """ - Write an xml file and use the to xml helper method to load it. - - Args: - nested_data: the nested data - xml_file: the xml file path - """ - xml = _to_file(nested_data)[0] - open(xml_file, 'w').write(etree.tostring(xml, xml_declaration=True, pretty_print=True)) + """ + Write an xml file and use the to xml helper method to load it. + + Args: + nested_data: the nested data + xml_file: the xml file path + """ + xml = _to_file(nested_data)[0] + open(xml_file, 'w').write(etree.tostring(xml, xml_declaration=True, pretty_print=True)) def _to_file(nested_data): - """ - Recursivly parse the nested data into xml tree format. - - Args: - nested_data: the nested data - - Returns: - the xml tree filled with child nodes - """ - nodes = list() - for key, values in nested_data.iteritems(): - #listify the values if not a list - if not isinstance(values, (list, set, tuple)): - values = [values] - for value in values: - node = etree.Element(key) - if isinstance(value, (str, unicode)): node.text = value - else: node.extend(_to_file(value)) - nodes.append(node) - return nodes + """ + Recursivly parse the nested data into xml tree format. + + Args: + nested_data: the nested data + + Returns: + the xml tree filled with child nodes + """ + nodes = list() + for key, values in nested_data.iteritems(): + #listify the values if not a list + if not isinstance(values, (list, set, tuple)): + values = [values] + for value in values: + node = etree.Element(key) + if isinstance(value, (str, unicode)): node.text = value + else: node.extend(_to_file(value)) + nodes.append(node) + return nodes if __name__ == '__main__': - """Use the main method to test parse xml's functions.""" - pass + """Use the main method to test parse xml's functions.""" + pass diff --git a/grc/base/Platform.py b/grc/base/Platform.py index f0f24a48a7..bb80e29552 100644 --- a/grc/base/Platform.py +++ b/grc/base/Platform.py @@ -30,159 +30,159 @@ from Constants import BLOCK_TREE_DTD, FLOW_GRAPH_DTD class Platform(_Element): - def __init__(self, name, version, key, - block_paths, block_dtd, default_flow_graph, generator, - license='', website=None, colors=[]): - """ - Make a platform from the arguments. - - Args: - name: the platform name - version: the version string - key: the unique platform key - block_paths: the file paths to blocks in this platform - block_dtd: the dtd validator for xml block wrappers - default_flow_graph: the default flow graph file path - generator: the generator class for this platform - colors: a list of title, color_spec tuples - license: a multi-line license (first line is copyright) - website: the website url for this platform - - Returns: - a platform object - """ - _Element.__init__(self) - self._name = name - self._version = version - self._key = key - self._license = license - self._website = website - self._block_paths = block_paths - self._block_dtd = block_dtd - self._default_flow_graph = default_flow_graph - self._generator = generator - self._colors = colors - #create a dummy flow graph for the blocks - self._flow_graph = _Element(self) - #search for *.xml files in the given search path - - self.loadblocks(); + def __init__(self, name, version, key, + block_paths, block_dtd, default_flow_graph, generator, + license='', website=None, colors=[]): + """ + Make a platform from the arguments. + + Args: + name: the platform name + version: the version string + key: the unique platform key + block_paths: the file paths to blocks in this platform + block_dtd: the dtd validator for xml block wrappers + default_flow_graph: the default flow graph file path + generator: the generator class for this platform + colors: a list of title, color_spec tuples + license: a multi-line license (first line is copyright) + website: the website url for this platform + + Returns: + a platform object + """ + _Element.__init__(self) + self._name = name + self._version = version + self._key = key + self._license = license + self._website = website + self._block_paths = block_paths + self._block_dtd = block_dtd + self._default_flow_graph = default_flow_graph + self._generator = generator + self._colors = colors + #create a dummy flow graph for the blocks + self._flow_graph = _Element(self) + #search for *.xml files in the given search path + + self.loadblocks(); - def loadblocks(self): - xml_files = list() - for block_path in self._block_paths: - if os.path.isfile(block_path): xml_files.append(block_path) - elif os.path.isdir(block_path): - for dirpath, dirnames, filenames in os.walk(block_path): - for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): - xml_files.append(os.path.join(dirpath, filename)) - #load the blocks - self._blocks = odict() - self._blocks_n = odict() - self._block_tree_files = list() - for xml_file in xml_files: - try: #try to add the xml file as a block wrapper - ParseXML.validate_dtd(xml_file, self._block_dtd) - n = ParseXML.from_file(xml_file).find('block') - #inject block wrapper path - n['block_wrapper_path'] = xml_file - block = self.Block(self._flow_graph, n) - key = block.get_key() - #test against repeated keys - if key in self.get_block_keys(): - print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s'%(key, xml_file) - #store the block - else: - self._blocks[key] = block - self._blocks_n[key] = n - except ParseXML.XMLSyntaxError, e: - try: #try to add the xml file as a block tree - ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD) - self._block_tree_files.append(xml_file) - except ParseXML.XMLSyntaxError, e: - print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) - except Exception, e: - print >> sys.stderr, 'Warning: Block loading failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) - - def parse_flow_graph(self, flow_graph_file): - """ - Parse a saved flow graph file. - Ensure that the file exists, and passes the dtd check. - - Args: - flow_graph_file: the flow graph file - - Returns: - nested data - @throws exception if the validation fails - """ - flow_graph_file = flow_graph_file or self._default_flow_graph - open(flow_graph_file, 'r') #test open - ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD) - return ParseXML.from_file(flow_graph_file) - - def load_block_tree(self, block_tree): - """ - Load a block tree with categories and blocks. - Step 1: Load all blocks from the xml specification. - Step 2: Load blocks with builtin category specifications. - - Args: - block_tree: the block tree object - """ - #recursive function to load categories and blocks - def load_category(cat_n, parent=[]): - #add this category - parent = parent + [cat_n.find('name')] - block_tree.add_block(parent) - #recursive call to load sub categories - map(lambda c: load_category(c, parent), cat_n.findall('cat')) - #add blocks in this category - for block_key in cat_n.findall('block'): - if block_key not in self.get_block_keys(): - print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.'%(block_key) - continue - block = self.get_block(block_key) - #if it exists, the block's category overrides the block tree - if not block.get_category(): block_tree.add_block(parent, block) - #load the block tree - for block_tree_file in self._block_tree_files: - #recursivly add all blocks in the tree - load_category(ParseXML.from_file(block_tree_file).find('cat')) - #add all other blocks, use the catgory tag - for block in self.get_blocks(): - #blocks with empty categories are in the xml block tree or hidden - if not block.get_category(): continue - block_tree.add_block(block.get_category(), block) - - def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name()) - - def is_platform(self): return True - - def get_new_flow_graph(self): return self.FlowGraph(platform=self) - - def get_generator(self): return self._generator - - ############################################## - # Access Blocks - ############################################## - def get_block_keys(self): return self._blocks.keys() - def get_block(self, key): return self._blocks[key] - def get_blocks(self): return self._blocks.values() - def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) - - def get_name(self): return self._name - def get_version(self): return self._version - def get_key(self): return self._key - def get_license(self): return self._license - def get_website(self): return self._website - def get_colors(self): return self._colors - - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param + def loadblocks(self): + xml_files = list() + for block_path in self._block_paths: + if os.path.isfile(block_path): xml_files.append(block_path) + elif os.path.isdir(block_path): + for dirpath, dirnames, filenames in os.walk(block_path): + for filename in sorted(filter(lambda f: f.endswith('.xml'), filenames)): + xml_files.append(os.path.join(dirpath, filename)) + #load the blocks + self._blocks = odict() + self._blocks_n = odict() + self._block_tree_files = list() + for xml_file in xml_files: + try: #try to add the xml file as a block wrapper + ParseXML.validate_dtd(xml_file, self._block_dtd) + n = ParseXML.from_file(xml_file).find('block') + #inject block wrapper path + n['block_wrapper_path'] = xml_file + block = self.Block(self._flow_graph, n) + key = block.get_key() + #test against repeated keys + if key in self.get_block_keys(): + print >> sys.stderr, 'Warning: Block with key "%s" already exists.\n\tIgnoring: %s'%(key, xml_file) + #store the block + else: + self._blocks[key] = block + self._blocks_n[key] = n + except ParseXML.XMLSyntaxError, e: + try: #try to add the xml file as a block tree + ParseXML.validate_dtd(xml_file, BLOCK_TREE_DTD) + self._block_tree_files.append(xml_file) + except ParseXML.XMLSyntaxError, e: + print >> sys.stderr, 'Warning: Block validation failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) + except Exception, e: + print >> sys.stderr, 'Warning: Block loading failed:\n\t%s\n\tIgnoring: %s'%(e, xml_file) + + def parse_flow_graph(self, flow_graph_file): + """ + Parse a saved flow graph file. + Ensure that the file exists, and passes the dtd check. + + Args: + flow_graph_file: the flow graph file + + Returns: + nested data + @throws exception if the validation fails + """ + flow_graph_file = flow_graph_file or self._default_flow_graph + open(flow_graph_file, 'r') #test open + ParseXML.validate_dtd(flow_graph_file, FLOW_GRAPH_DTD) + return ParseXML.from_file(flow_graph_file) + + def load_block_tree(self, block_tree): + """ + Load a block tree with categories and blocks. + Step 1: Load all blocks from the xml specification. + Step 2: Load blocks with builtin category specifications. + + Args: + block_tree: the block tree object + """ + #recursive function to load categories and blocks + def load_category(cat_n, parent=[]): + #add this category + parent = parent + [cat_n.find('name')] + block_tree.add_block(parent) + #recursive call to load sub categories + map(lambda c: load_category(c, parent), cat_n.findall('cat')) + #add blocks in this category + for block_key in cat_n.findall('block'): + if block_key not in self.get_block_keys(): + print >> sys.stderr, 'Warning: Block key "%s" not found when loading category tree.'%(block_key) + continue + block = self.get_block(block_key) + #if it exists, the block's category overrides the block tree + if not block.get_category(): block_tree.add_block(parent, block) + #load the block tree + for block_tree_file in self._block_tree_files: + #recursivly add all blocks in the tree + load_category(ParseXML.from_file(block_tree_file).find('cat')) + #add all other blocks, use the catgory tag + for block in self.get_blocks(): + #blocks with empty categories are in the xml block tree or hidden + if not block.get_category(): continue + block_tree.add_block(block.get_category(), block) + + def __str__(self): return 'Platform - %s(%s)'%(self.get_key(), self.get_name()) + + def is_platform(self): return True + + def get_new_flow_graph(self): return self.FlowGraph(platform=self) + + def get_generator(self): return self._generator + + ############################################## + # Access Blocks + ############################################## + def get_block_keys(self): return self._blocks.keys() + def get_block(self, key): return self._blocks[key] + def get_blocks(self): return self._blocks.values() + def get_new_block(self, flow_graph, key): return self.Block(flow_graph, n=self._blocks_n[key]) + + def get_name(self): return self._name + def get_version(self): return self._version + def get_key(self): return self._key + def get_license(self): return self._license + def get_website(self): return self._website + def get_colors(self): return self._colors + + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Port = _Port + Param = _Param diff --git a/grc/base/Port.py b/grc/base/Port.py index 0e58f583c3..b7de5301f1 100644 --- a/grc/base/Port.py +++ b/grc/base/Port.py @@ -21,69 +21,96 @@ from Element import Element class Port(Element): - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction source or sink - """ - #build the port - Element.__init__(self, block) - #grab the data - self._name = n['name'] - self._key = n['key'] - self._type = n['type'] - self._dir = dir + def __init__(self, block, n, dir): + """ + Make a new port from nested data. + + Args: + block: the parent element + n: the nested odict + dir: the direction source or sink + """ + #build the port + Element.__init__(self, block) + #grab the data + self._name = n['name'] + self._key = n['key'] + self._type = n['type'] + self._dir = dir - def validate(self): - """ - Validate the port. - The port must be non-empty and type must a possible type. - """ - Element.validate(self) - if self.get_type() not in self.get_types(): - self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) + def validate(self): + """ + Validate the port. + The port must be non-empty and type must a possible type. + """ + Element.validate(self) + if self.get_type() not in self.get_types(): + self.add_error_message('Type "%s" is not a possible type.'%self.get_type()) - def __str__(self): - if self.is_source(): - return 'Source - %s(%s)'%(self.get_name(), self.get_key()) - if self.is_sink(): - return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) + def __str__(self): + if self.is_source(): + return 'Source - %s(%s)'%(self.get_name(), self.get_key()) + if self.is_sink(): + return 'Sink - %s(%s)'%(self.get_name(), self.get_key()) - def get_types(self): - """ - Get a list of all possible port types. - @throw NotImplementedError - """ - raise NotImplementedError + def get_types(self): + """ + Get a list of all possible port types. + @throw NotImplementedError + """ + raise NotImplementedError - def is_port(self): return True - def get_color(self): return '#FFFFFF' - def get_name(self): return self._name - def get_key(self): return self._key - def is_sink(self): return self._dir == 'sink' - def is_source(self): return self._dir == 'source' - def get_type(self): return self.get_parent().resolve_dependencies(self._type) + def is_port(self): return True + def get_color(self): return '#FFFFFF' + def get_name(self): + number = '' + if self.get_type() == 'bus': + busses = filter(lambda a: a._dir == self._dir, self.get_parent().get_ports_gui()); + + number = str(busses.index(self)) + '#' + str(len(self.get_associated_ports())); + return self._name + number - def get_connections(self): - """ - Get all connections that use this port. - - Returns: - a list of connection objects - """ - connections = self.get_parent().get_parent().get_connections() - connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) - return connections + def get_key(self): return self._key + def is_sink(self): return self._dir == 'sink' + def is_source(self): return self._dir == 'source' + def get_type(self): return self.get_parent().resolve_dependencies(self._type) - def get_enabled_connections(self): - """ - Get all enabled connections that use this port. - - Returns: - a list of connection objects - """ - return filter(lambda c: c.get_enabled(), self.get_connections()) + def get_connections(self): + """ + Get all connections that use this port. + + Returns: + a list of connection objects + """ + connections = self.get_parent().get_parent().get_connections() + connections = filter(lambda c: c.get_source() is self or c.get_sink() is self, connections) + return connections + + def get_enabled_connections(self): + """ + Get all enabled connections that use this port. + + Returns: + a list of connection objects + """ + return filter(lambda c: c.get_enabled(), self.get_connections()) + + def get_associated_ports(self): + if not self.get_type() == 'bus': + return [self]; + else: + if self.is_source(): + get_p = self.get_parent().get_sources; + bus_structure = self.get_parent().current_bus_structure['source']; + direc = 'source' + else: + get_p = self.get_parent().get_sinks; + bus_structure = self.get_parent().current_bus_structure['sink']; + direc = 'sink' + + ports = [i for i in get_p() if not i.get_type() == 'bus']; + if bus_structure: + busses = [i for i in get_p() if i.get_type() == 'bus']; + bus_index = busses.index(self); + ports = filter(lambda a: ports.index(a) in bus_structure[bus_index], ports); + return ports; diff --git a/grc/base/block_tree.dtd b/grc/base/block_tree.dtd index 7d4a13ccc3..9e23576477 100644 --- a/grc/base/block_tree.dtd +++ b/grc/base/block_tree.dtd @@ -17,9 +17,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - block_tree.dtd - Josh Blum - The document type definition for a block tree category listing. + block_tree.dtd + Josh Blum + The document type definition for a block tree category listing. --> <!ELEMENT cat (name, cat*, block*)> <!ELEMENT name (#PCDATA)> diff --git a/grc/base/flow_graph.dtd b/grc/base/flow_graph.dtd index 74f48f10ab..bdfe1dc059 100644 --- a/grc/base/flow_graph.dtd +++ b/grc/base/flow_graph.dtd @@ -17,17 +17,19 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - flow_graph.dtd - Josh Blum - The document type definition for flow graph xml files. + flow_graph.dtd + Josh Blum + The document type definition for flow graph xml files. --> <!ELEMENT flow_graph (timestamp?, block*, connection*)> <!-- optional timestamp --> <!ELEMENT timestamp (#PCDATA)> <!-- Block --> -<!ELEMENT block (key, param*)> +<!ELEMENT block (key, param*, bus_sink?, bus_source?)> <!ELEMENT param (key, value)> <!ELEMENT key (#PCDATA)> <!ELEMENT value (#PCDATA)> +<!ELEMENT bus_sink (#PCDATA)> +<!ELEMENT bus_source (#PCDATA)> <!-- Connection --> <!ELEMENT connection (source_block_id, sink_block_id, source_key, sink_key)> <!ELEMENT source_block_id (#PCDATA)> diff --git a/grc/base/odict.py b/grc/base/odict.py index 302583163b..0c03d753f6 100644 --- a/grc/base/odict.py +++ b/grc/base/odict.py @@ -21,85 +21,85 @@ from UserDict import DictMixin class odict(DictMixin): - def __init__(self, d={}): - self._keys = list(d.keys()) - self._data = dict(d.copy()) + def __init__(self, d={}): + self._keys = list(d.keys()) + self._data = dict(d.copy()) - def __setitem__(self, key, value): - if key not in self._data: - self._keys.append(key) - self._data[key] = value + def __setitem__(self, key, value): + if key not in self._data: + self._keys.append(key) + self._data[key] = value - def __getitem__(self, key): - return self._data[key] + def __getitem__(self, key): + return self._data[key] - def __delitem__(self, key): - del self._data[key] - self._keys.remove(key) + def __delitem__(self, key): + del self._data[key] + self._keys.remove(key) - def keys(self): - return list(self._keys) + def keys(self): + return list(self._keys) - def copy(self): - copy_dict = odict() - copy_dict._data = self._data.copy() - copy_dict._keys = list(self._keys) - return copy_dict + def copy(self): + copy_dict = odict() + copy_dict._data = self._data.copy() + copy_dict._keys = list(self._keys) + return copy_dict - def insert_after(self, pos_key, key, val): - """ - Insert the new key, value entry after the entry given by the position key. - If the positional key is None, insert at the end. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) - self._keys.insert(index+1, key) - self._data[key] = val + def insert_after(self, pos_key, key, val): + """ + Insert the new key, value entry after the entry given by the position key. + If the positional key is None, insert at the end. + + Args: + pos_key: the positional key + key: the key for the new entry + val: the value for the new entry + """ + index = (pos_key is None) and len(self._keys) or self._keys.index(pos_key) + if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + self._keys.insert(index+1, key) + self._data[key] = val - def insert_before(self, pos_key, key, val): - """ - Insert the new key, value entry before the entry given by the position key. - If the positional key is None, insert at the begining. - - Args: - pos_key: the positional key - key: the key for the new entry - val: the value for the new entry - """ - index = (pos_key is not None) and self._keys.index(pos_key) or 0 - if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) - self._keys.insert(index, key) - self._data[key] = val + def insert_before(self, pos_key, key, val): + """ + Insert the new key, value entry before the entry given by the position key. + If the positional key is None, insert at the begining. + + Args: + pos_key: the positional key + key: the key for the new entry + val: the value for the new entry + """ + index = (pos_key is not None) and self._keys.index(pos_key) or 0 + if key in self._keys: raise KeyError('Cannot insert, key "%s" already exists'%str(key)) + self._keys.insert(index, key) + self._data[key] = val - def find(self, key): - """ - Get the value for this key if exists. - - Args: - key: the key to search for - - Returns: - the value or None - """ - if self.has_key(key): return self[key] - return None + def find(self, key): + """ + Get the value for this key if exists. + + Args: + key: the key to search for + + Returns: + the value or None + """ + if self.has_key(key): return self[key] + return None - def findall(self, key): - """ - Get a list of values for this key. - - Args: - key: the key to search for - - Returns: - a list of values or empty list - """ - obj = self.find(key) - if obj is None: obj = list() - if isinstance(obj, list): return obj - return [obj] + def findall(self, key): + """ + Get a list of values for this key. + + Args: + key: the key to search for + + Returns: + a list of values or empty list + """ + obj = self.find(key) + if obj is None: obj = list() + if isinstance(obj, list): return obj + return [obj] diff --git a/grc/blocks/bus_sink.xml b/grc/blocks/bus_sink.xml new file mode 100644 index 0000000000..273b4c517a --- /dev/null +++ b/grc/blocks/bus_sink.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Sink</name> + <key>bus_sink</key> + <make>$yesno.yesno</make> + + <param> + <name>On/Off</name> + <key>yesno</key> + <type>enum</type> + <option> + <name>On</name> + <key>on</key> + <opt>yesno:True</opt> + </option> + <option> + <name>Off</name> + <key>off</key> + <opt>yesno:False</opt> + </option> + </param> +</block> diff --git a/grc/blocks/bus_source.xml b/grc/blocks/bus_source.xml new file mode 100644 index 0000000000..15e4a9f31b --- /dev/null +++ b/grc/blocks/bus_source.xml @@ -0,0 +1,27 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Source</name> + <key>bus_source</key> + <make>$yesno.yesno</make> + + <param> + <name>On/Off</name> + <key>yesno</key> + <type>enum</type> + <option> + <name>On</name> + <key>on</key> + <opt>yesno:True</opt> + </option> + <option> + <name>Off</name> + <key>off</key> + <opt>yesno:False</opt> + </option> + </param> +</block> diff --git a/grc/blocks/bus_structure_sink.xml b/grc/blocks/bus_structure_sink.xml new file mode 100644 index 0000000000..df16657282 --- /dev/null +++ b/grc/blocks/bus_structure_sink.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Sink Structure</name> + <key>bus_structure_sink</key> + <make>None</make> + + <param> + <name>Structure</name> + <key>struct</key> + <value></value> + <type>raw</type> + </param> +</block> diff --git a/grc/blocks/bus_structure_source.xml b/grc/blocks/bus_structure_source.xml new file mode 100644 index 0000000000..27652ca3b3 --- /dev/null +++ b/grc/blocks/bus_structure_source.xml @@ -0,0 +1,18 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Bus Sink +################################################### + --> +<block> + <name>Bus Source Structure</name> + <key>bus_structure_source</key> + <make>None</make> + + <param> + <name>Structure</name> + <key>struct</key> + <value></value> + <type>raw</type> + </param> +</block> diff --git a/grc/blocks/pad_sink.xml b/grc/blocks/pad_sink.xml index f0e10a3391..37e132c34c 100644 --- a/grc/blocks/pad_sink.xml +++ b/grc/blocks/pad_sink.xml @@ -62,6 +62,13 @@ None;self.message_port_register_hier_in($label) <value>1</value> <type>int</type> </param> + + <param> + <name>Num Streams</name> + <key>num_streams</key> + <value>1</value> + <type>int</type> + </param> <param> <name>Optional</name> <key>optional</key> @@ -78,10 +85,12 @@ None;self.message_port_register_hier_in($label) </option> </param> <check>$vlen > 0</check> + <check>$num_streams > 0</check> <sink> <name>in</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_streams</nports> </sink> <doc> The inputs of this block will become the outputs to this flow graph when it is instantiated as a hierarchical block. diff --git a/grc/blocks/pad_source.xml b/grc/blocks/pad_source.xml index a56a65dcc3..b6faebfc68 100644 --- a/grc/blocks/pad_source.xml +++ b/grc/blocks/pad_source.xml @@ -62,6 +62,14 @@ None;self.message_port_register_hier_out($label) <value>1</value> <type>int</type> </param> + + <param> + <name>Num Streams</name> + <key>num_streams</key> + <value>1</value> + <type>int</type> + </param> + <param> <name>Optional</name> <key>optional</key> @@ -78,10 +86,12 @@ None;self.message_port_register_hier_out($label) </option> </param> <check>$vlen > 0</check> + <check>$num_streams > 0</check> <source> <name>out</name> <type>$type</type> <vlen>$vlen</vlen> + <nports>$num_streams</nports> </source> <doc> The outputs of this block will become the inputs to this flow graph when it is instantiated as a hierarchical block. diff --git a/grc/blocks/variable_constellation.xml b/grc/blocks/variable_constellation.xml new file mode 100644 index 0000000000..7d23ede9be --- /dev/null +++ b/grc/blocks/variable_constellation.xml @@ -0,0 +1,51 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Options Block: +## options for window size, +## and flow graph building. +################################################### + --> +<block> + <name>Constellation Object</name> + <key>variable_constellation</key> + <category>Modulators</category> + <import>from gnuradio import digital</import> + <var_make>self.$(id) = $(id) = digital.constellation_calcdist($const_points, $sym_map, $rot_sym, $dims).base()</var_make> + <make></make> + <!--<callback></callback>--> + + <!-- Required to 'trick' GRC into using this as a proper variable--> + <param> + <name>Ignore Me</name> + <key>value</key> + <value>"ok"</value> + <type>raw</type> + <hide>all</hide> + </param> + + <param> + <name>Symbol Map</name> + <key>sym_map</key> + <value>[0, 1, 3, 2]</value> + <type>int_vector</type> + </param> + <param> + <name>Constellation Points</name> + <key>const_points</key> + <value>[-1-1j, -1+1j, 1+1j, 1-1j]</value> + <type>complex_vector</type> + </param> + <param> + <name>Rotational Symmetry</name> + <key>rot_sym</key> + <value>4</value> + <type>int</type> + </param> + <param> + <name>Dimensionality</name> + <key>dims</key> + <value>1</value> + <type>int</type> + </param> +</block> diff --git a/grc/blocks/variable_constellation_rect.xml b/grc/blocks/variable_constellation_rect.xml new file mode 100644 index 0000000000..5c136ee324 --- /dev/null +++ b/grc/blocks/variable_constellation_rect.xml @@ -0,0 +1,69 @@ +<?xml version="1.0"?> +<!-- +################################################### +##Options Block: +## options for window size, +## and flow graph building. +################################################### + --> +<block> + <name>Constellation Rect. Object</name> + <key>variable_constellation_rect</key> + <category>Modulators</category> + <import>from gnuradio import digital</import> + <var_make>self.$(id) = $(id) = digital.constellation_rect($const_points, $sym_map, $rot_sym, $real_sect, $imag_sect, $w_real_sect, $w_imag_sect).base()</var_make> + <make></make> + <!--<callback></callback>--> + + <!-- Required to 'trick' GRC into using this as a proper variable--> + <param> + <name>Ignore Me</name> + <key>value</key> + <value>"ok"</value> + <type>raw</type> + <hide>all</hide> + </param> + + <param> + <name>Symbol Map</name> + <key>sym_map</key> + <value>[0, 1, 3, 2]</value> + <type>int_vector</type> + </param> + <param> + <name>Constellation Points</name> + <key>const_points</key> + <value>[-1-1j, -1+1j, 1+1j, 1-1j]</value> + <type>complex_vector</type> + </param> + <param> + <name>Rotational Symmetry</name> + <key>rot_sym</key> + <value>4</value> + <type>int</type> + </param> + <param> + <name>Real Sectors</name> + <key>real_sect</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Imaginary Sectors</name> + <key>imag_sect</key> + <value>2</value> + <type>int</type> + </param> + <param> + <name>Width Real Sectors</name> + <key>w_real_sect</key> + <value>1</value> + <type>int</type> + </param> + <param> + <name>Width Imaginary Sectors</name> + <key>w_imag_sect</key> + <value>1</value> + <type>int</type> + </param> +</block> diff --git a/grc/examples/xmlrpc/xmlrpc_client_script.py b/grc/examples/xmlrpc/xmlrpc_client_script.py index 8f00fa55d8..e96c4cbf83 100644 --- a/grc/examples/xmlrpc/xmlrpc_client_script.py +++ b/grc/examples/xmlrpc/xmlrpc_client_script.py @@ -9,15 +9,15 @@ s = xmlrpclib.Server("http://localhost:1234") #randomly change parameters of the sinusoid for i in range(10): - #generate random values - new_freq = random.uniform(0, 5000) - new_ampl = random.uniform(0, 2) - new_offset = random.uniform(-1, 1) - #set new values - time.sleep(1) - s.set_freq(new_freq) - time.sleep(1) - s.set_ampl(new_ampl) - time.sleep(1) - s.set_offset(new_offset) + #generate random values + new_freq = random.uniform(0, 5000) + new_ampl = random.uniform(0, 2) + new_offset = random.uniform(-1, 1) + #set new values + time.sleep(1) + s.set_freq(new_freq) + time.sleep(1) + s.set_ampl(new_ampl) + time.sleep(1) + s.set_offset(new_offset) diff --git a/grc/grc_gnuradio/blks2/__init__.py b/grc/grc_gnuradio/blks2/__init__.py index fde76f2563..e6941ab91b 100644 --- a/grc/grc_gnuradio/blks2/__init__.py +++ b/grc/grc_gnuradio/blks2/__init__.py @@ -20,7 +20,7 @@ from selector import selector, valve from packet import options, packet_encoder, packet_decoder, \ - packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ - packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c + packet_mod_b, packet_mod_s, packet_mod_i, packet_mod_f, packet_mod_c, \ + packet_demod_b, packet_demod_s, packet_demod_i, packet_demod_f, packet_demod_c from error_rate import error_rate from tcp import tcp_source, tcp_sink diff --git a/grc/grc_gnuradio/blks2/error_rate.py b/grc/grc_gnuradio/blks2/error_rate.py index 7970f29bae..95ded7dadc 100644 --- a/grc/grc_gnuradio/blks2/error_rate.py +++ b/grc/grc_gnuradio/blks2/error_rate.py @@ -29,112 +29,112 @@ import numpy _1s_counts = [sum([1&(i>>j) for j in range(8)]) for i in range(2**8)] class input_watcher(_threading.Thread): - """ - Read samples from the message queue and hand them to the callback. - """ + """ + Read samples from the message queue and hand them to the callback. + """ - def __init__(self, msgq, callback): - self._msgq = msgq - self._callback = callback - _threading.Thread.__init__(self) - self.setDaemon(1) - self.keep_running = True - self.start() + def __init__(self, msgq, callback): + self._msgq = msgq + self._callback = callback + _threading.Thread.__init__(self) + self.setDaemon(1) + self.keep_running = True + self.start() - def run(self): - r = '' - while True: - msg = self._msgq.delete_head() - itemsize = int(msg.arg1()) - nitems = int(msg.arg2()) - s = r + msg.to_string() - i = (nitems-nitems%2)*itemsize - r = s[i:] - s = s[:i] - samples = numpy.fromstring(s, numpy.int8) - self._callback(samples) + def run(self): + r = '' + while True: + msg = self._msgq.delete_head() + itemsize = int(msg.arg1()) + nitems = int(msg.arg2()) + s = r + msg.to_string() + i = (nitems-nitems%2)*itemsize + r = s[i:] + s = s[:i] + samples = numpy.fromstring(s, numpy.int8) + self._callback(samples) class error_rate(gr.hier_block2): - """ - Sample the incoming data streams (byte) and calculate the bit or symbol error rate. - Write the running rate to the output data stream (float). - """ + """ + Sample the incoming data streams (byte) and calculate the bit or symbol error rate. + Write the running rate to the output data stream (float). + """ - def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2): - """ - Error rate constructor. - - Args: - type: a string 'BER' or 'SER' - win_size: the number of samples to calculate over - bits_per_symbol: the number of information bits per symbol (BER only) - """ - #init - gr.hier_block2.__init__( - self, 'error_rate', - gr.io_signature(2, 2, gr.sizeof_char), - gr.io_signature(1, 1, gr.sizeof_float), - ) - assert type in ('BER', 'SER') - self._max_samples = win_size - self._bits_per_symbol = bits_per_symbol - #setup message queue - msg_source = blocks.message_source(gr.sizeof_float, 1) - self._msgq_source = msg_source.msgq() - msgq_sink = gr.msg_queue(2) - msg_sink = blocks.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking - inter = blocks.interleave(gr.sizeof_char) - #start thread - self._num_errs = 0 - self._err_index = 0 - self._num_samps = 0 - self._err_array = numpy.zeros(self._max_samples, numpy.int8) - if type == 'BER': - input_watcher(msgq_sink, self._handler_ber) - elif type == 'SER': - input_watcher(msgq_sink, self._handler_ser) - #connect - self.connect(msg_source, self) - self.connect((self, 0), (inter, 0)) - self.connect((self, 1), (inter, 1)) - self.connect(inter, msg_sink) + def __init__(self, type='BER', win_size=default_win_size, bits_per_symbol=2): + """ + Error rate constructor. + + Args: + type: a string 'BER' or 'SER' + win_size: the number of samples to calculate over + bits_per_symbol: the number of information bits per symbol (BER only) + """ + #init + gr.hier_block2.__init__( + self, 'error_rate', + gr.io_signature(2, 2, gr.sizeof_char), + gr.io_signature(1, 1, gr.sizeof_float), + ) + assert type in ('BER', 'SER') + self._max_samples = win_size + self._bits_per_symbol = bits_per_symbol + #setup message queue + msg_source = blocks.message_source(gr.sizeof_float, 1) + self._msgq_source = msg_source.msgq() + msgq_sink = gr.msg_queue(2) + msg_sink = blocks.message_sink(gr.sizeof_char, msgq_sink, False) #False -> blocking + inter = blocks.interleave(gr.sizeof_char) + #start thread + self._num_errs = 0 + self._err_index = 0 + self._num_samps = 0 + self._err_array = numpy.zeros(self._max_samples, numpy.int8) + if type == 'BER': + input_watcher(msgq_sink, self._handler_ber) + elif type == 'SER': + input_watcher(msgq_sink, self._handler_ser) + #connect + self.connect(msg_source, self) + self.connect((self, 0), (inter, 0)) + self.connect((self, 1), (inter, 1)) + self.connect(inter, msg_sink) - def _handler_ber(self, samples): - num = len(samples)/2 - arr = numpy.zeros(num, numpy.float32) - for i in range(num): - old_err = self._err_array[self._err_index] - #record error - self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]] - self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err - #increment index - self._err_index = (self._err_index + 1)%self._max_samples - self._num_samps = min(self._num_samps + 1, self._max_samples) - #write sample - arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol) - #write message - msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) - self._msgq_source.insert_tail(msg) + def _handler_ber(self, samples): + num = len(samples)/2 + arr = numpy.zeros(num, numpy.float32) + for i in range(num): + old_err = self._err_array[self._err_index] + #record error + self._err_array[self._err_index] = _1s_counts[samples[i*2] ^ samples[i*2 + 1]] + self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err + #increment index + self._err_index = (self._err_index + 1)%self._max_samples + self._num_samps = min(self._num_samps + 1, self._max_samples) + #write sample + arr[i] = float(self._num_errs)/float(self._num_samps*self._bits_per_symbol) + #write message + msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) + self._msgq_source.insert_tail(msg) - def _handler_ser(self, samples): - num = len(samples)/2 - arr = numpy.zeros(num, numpy.float32) - for i in range(num): - old_err = self._err_array[self._err_index] - #record error - ref = samples[i*2] - res = samples[i*2 + 1] - if ref == res: - self._err_array[self._err_index] = 0 - else: - self._err_array[self._err_index] = 1 - #update number of errors - self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err - #increment index - self._err_index = (self._err_index + 1)%self._max_samples - self._num_samps = min(self._num_samps + 1, self._max_samples) - #write sample - arr[i] = float(self._num_errs)/float(self._num_samps) - #write message - msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) - self._msgq_source.insert_tail(msg) + def _handler_ser(self, samples): + num = len(samples)/2 + arr = numpy.zeros(num, numpy.float32) + for i in range(num): + old_err = self._err_array[self._err_index] + #record error + ref = samples[i*2] + res = samples[i*2 + 1] + if ref == res: + self._err_array[self._err_index] = 0 + else: + self._err_array[self._err_index] = 1 + #update number of errors + self._num_errs = self._num_errs + self._err_array[self._err_index] - old_err + #increment index + self._err_index = (self._err_index + 1)%self._max_samples + self._num_samps = min(self._num_samps + 1, self._max_samples) + #write sample + arr[i] = float(self._num_errs)/float(self._num_samps) + #write message + msg = blocks.message_from_string(arr.tostring(), 0, gr.sizeof_float, num) + self._msgq_source.insert_tail(msg) diff --git a/grc/grc_gnuradio/blks2/packet.py b/grc/grc_gnuradio/blks2/packet.py index 4c7bd235dd..10dd002471 100644 --- a/grc/grc_gnuradio/blks2/packet.py +++ b/grc/grc_gnuradio/blks2/packet.py @@ -36,176 +36,176 @@ DEFAULT_THRESHOLD = 12 ## Options Class for OFDM ################################################## class options(object): - def __init__(self, **kwargs): - for key, value in kwargs.iteritems(): setattr(self, key, value) + def __init__(self, **kwargs): + for key, value in kwargs.iteritems(): setattr(self, key, value) ################################################## ## Packet Encoder ################################################## class _packet_encoder_thread(_threading.Thread): - def __init__(self, msgq, payload_length, send): - self._msgq = msgq - self._payload_length = payload_length - self._send = send - _threading.Thread.__init__(self) - self.setDaemon(1) - self.keep_running = True - self.start() - - def run(self): - sample = '' #residual sample - while self.keep_running: - msg = self._msgq.delete_head() #blocking read of message queue - sample = sample + msg.to_string() #get the body of the msg as a string - while len(sample) >= self._payload_length: - payload = sample[:self._payload_length] - sample = sample[self._payload_length:] - self._send(payload) + def __init__(self, msgq, payload_length, send): + self._msgq = msgq + self._payload_length = payload_length + self._send = send + _threading.Thread.__init__(self) + self.setDaemon(1) + self.keep_running = True + self.start() + + def run(self): + sample = '' #residual sample + while self.keep_running: + msg = self._msgq.delete_head() #blocking read of message queue + sample = sample + msg.to_string() #get the body of the msg as a string + while len(sample) >= self._payload_length: + payload = sample[:self._payload_length] + sample = sample[self._payload_length:] + self._send(payload) class packet_encoder(gr.hier_block2): - """ - Hierarchical block for wrapping packet-based modulators. - """ - - def __init__(self, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True): - """ - packet_mod constructor. - - Args: - samples_per_symbol: number of samples per symbol - bits_per_symbol: number of bits per symbol - access_code: AKA sync vector - pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples - payload_length: number of bytes in a data-stream slice - """ - #setup parameters - self._samples_per_symbol = samples_per_symbol - self._bits_per_symbol = bits_per_symbol - self._pad_for_usrp = pad_for_usrp - if not access_code: #get access code - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - self._pad_for_usrp = pad_for_usrp - #create blocks - msg_source = blocks.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT) - self._msgq_out = msg_source.msgq() - #initialize hier2 - gr.hier_block2.__init__( - self, - "packet_encoder", - gr.io_signature(0, 0, 0), # Input signature - gr.io_signature(1, 1, gr.sizeof_char) # Output signature - ) - #connect - self.connect(msg_source, self) - - def send_pkt(self, payload): - """ - Wrap the payload in a packet and push onto the message queue. - - Args: - payload: string, data to send - """ - packet = packet_utils.make_packet( - payload, - self._samples_per_symbol, - self._bits_per_symbol, - self._access_code, - self._pad_for_usrp - ) - msg = gr.message_from_string(packet) - self._msgq_out.insert_tail(msg) + """ + Hierarchical block for wrapping packet-based modulators. + """ + + def __init__(self, samples_per_symbol, bits_per_symbol, access_code='', pad_for_usrp=True): + """ + packet_mod constructor. + + Args: + samples_per_symbol: number of samples per symbol + bits_per_symbol: number of bits per symbol + access_code: AKA sync vector + pad_for_usrp: If true, packets are padded such that they end up a multiple of 128 samples + payload_length: number of bytes in a data-stream slice + """ + #setup parameters + self._samples_per_symbol = samples_per_symbol + self._bits_per_symbol = bits_per_symbol + self._pad_for_usrp = pad_for_usrp + if not access_code: #get access code + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + self._pad_for_usrp = pad_for_usrp + #create blocks + msg_source = blocks.message_source(gr.sizeof_char, DEFAULT_MSGQ_LIMIT) + self._msgq_out = msg_source.msgq() + #initialize hier2 + gr.hier_block2.__init__( + self, + "packet_encoder", + gr.io_signature(0, 0, 0), # Input signature + gr.io_signature(1, 1, gr.sizeof_char) # Output signature + ) + #connect + self.connect(msg_source, self) + + def send_pkt(self, payload): + """ + Wrap the payload in a packet and push onto the message queue. + + Args: + payload: string, data to send + """ + packet = packet_utils.make_packet( + payload, + self._samples_per_symbol, + self._bits_per_symbol, + self._access_code, + self._pad_for_usrp + ) + msg = gr.message_from_string(packet) + self._msgq_out.insert_tail(msg) ################################################## ## Packet Decoder ################################################## class _packet_decoder_thread(_threading.Thread): - def __init__(self, msgq, callback): - _threading.Thread.__init__(self) - self.setDaemon(1) - self._msgq = msgq - self.callback = callback - self.keep_running = True - self.start() - - def run(self): - while self.keep_running: - msg = self._msgq.delete_head() - ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) - if self.callback: - self.callback(ok, payload) + def __init__(self, msgq, callback): + _threading.Thread.__init__(self) + self.setDaemon(1) + self._msgq = msgq + self.callback = callback + self.keep_running = True + self.start() + + def run(self): + while self.keep_running: + msg = self._msgq.delete_head() + ok, payload = packet_utils.unmake_packet(msg.to_string(), int(msg.arg1())) + if self.callback: + self.callback(ok, payload) class packet_decoder(gr.hier_block2): - """ - Hierarchical block for wrapping packet-based demodulators. - """ - - def __init__(self, access_code='', threshold=-1, callback=None): - """ - packet_demod constructor. - - Args: - access_code: AKA sync vector - threshold: detect access_code with up to threshold bits wrong (0 -> use default) - callback: a function of args: ok, payload - """ - #access code - if not access_code: #get access code - access_code = packet_utils.default_access_code - if not packet_utils.is_1_0_string(access_code): - raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) - self._access_code = access_code - #threshold - if threshold < 0: threshold = DEFAULT_THRESHOLD - self._threshold = threshold - #blocks - msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY - correlator = digital.correlate_access_code_bb(self._access_code, self._threshold) - framer_sink = digital.framer_sink_1(msgq) - #initialize hier2 - gr.hier_block2.__init__( - self, - "packet_decoder", - gr.io_signature(1, 1, gr.sizeof_char), # Input signature - gr.io_signature(0, 0, 0) # Output signature - ) - #connect - self.connect(self, correlator, framer_sink) - #start thread - _packet_decoder_thread(msgq, callback) + """ + Hierarchical block for wrapping packet-based demodulators. + """ + + def __init__(self, access_code='', threshold=-1, callback=None): + """ + packet_demod constructor. + + Args: + access_code: AKA sync vector + threshold: detect access_code with up to threshold bits wrong (0 -> use default) + callback: a function of args: ok, payload + """ + #access code + if not access_code: #get access code + access_code = packet_utils.default_access_code + if not packet_utils.is_1_0_string(access_code): + raise ValueError, "Invalid access_code %r. Must be string of 1's and 0's" % (access_code,) + self._access_code = access_code + #threshold + if threshold < 0: threshold = DEFAULT_THRESHOLD + self._threshold = threshold + #blocks + msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) #holds packets from the PHY + correlator = digital.correlate_access_code_bb(self._access_code, self._threshold) + framer_sink = digital.framer_sink_1(msgq) + #initialize hier2 + gr.hier_block2.__init__( + self, + "packet_decoder", + gr.io_signature(1, 1, gr.sizeof_char), # Input signature + gr.io_signature(0, 0, 0) # Output signature + ) + #connect + self.connect(self, correlator, framer_sink) + #start thread + _packet_decoder_thread(msgq, callback) ################################################## ## Packet Mod for OFDM Mod and Packet Encoder ################################################## class packet_mod_base(gr.hier_block2): - """ - Hierarchical block for wrapping packet source block. - """ - - def __init__(self, packet_source=None, payload_length=0): - if not payload_length: #get payload length - payload_length = DEFAULT_PAYLOAD_LEN - if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size - raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in) - #initialize hier2 - gr.hier_block2.__init__( - self, - "ofdm_mod", - gr.io_signature(1, 1, self._item_size_in), # Input signature - gr.io_signature(1, 1, packet_source._hb.output_signature().sizeof_stream_item(0)) # Output signature - ) - #create blocks - msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) - msg_sink = blocks.message_sink(self._item_size_in, msgq, False) #False -> blocking - #connect - self.connect(self, msg_sink) - self.connect(packet_source, self) - #start thread - _packet_encoder_thread(msgq, payload_length, packet_source.send_pkt) + """ + Hierarchical block for wrapping packet source block. + """ + + def __init__(self, packet_source=None, payload_length=0): + if not payload_length: #get payload length + payload_length = DEFAULT_PAYLOAD_LEN + if payload_length%self._item_size_in != 0: #verify that packet length is a multiple of the stream size + raise ValueError, 'The payload length: "%d" is not a mutiple of the stream size: "%d".'%(payload_length, self._item_size_in) + #initialize hier2 + gr.hier_block2.__init__( + self, + "ofdm_mod", + gr.io_signature(1, 1, self._item_size_in), # Input signature + gr.io_signature(1, 1, packet_source._hb.output_signature().sizeof_stream_item(0)) # Output signature + ) + #create blocks + msgq = gr.msg_queue(DEFAULT_MSGQ_LIMIT) + msg_sink = blocks.message_sink(self._item_size_in, msgq, False) #False -> blocking + #connect + self.connect(self, msg_sink) + self.connect(packet_source, self) + #start thread + _packet_encoder_thread(msgq, payload_length, packet_source.send_pkt) class packet_mod_b(packet_mod_base): _item_size_in = gr.sizeof_char class packet_mod_s(packet_mod_base): _item_size_in = gr.sizeof_short @@ -217,32 +217,32 @@ class packet_mod_c(packet_mod_base): _item_size_in = gr.sizeof_gr_complex ## Packet Demod for OFDM Demod and Packet Decoder ################################################## class packet_demod_base(gr.hier_block2): - """ - Hierarchical block for wrapping packet sink block. - """ - - def __init__(self, packet_sink=None): - #initialize hier2 - gr.hier_block2.__init__( - self, - "ofdm_mod", - gr.io_signature(1, 1, packet_sink._hb.input_signature().sizeof_stream_item(0)), # Input signature - gr.io_signature(1, 1, self._item_size_out) # Output signature - ) - #create blocks - msg_source = blocks.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT) - self._msgq_out = msg_source.msgq() - #connect - self.connect(self, packet_sink) - self.connect(msg_source, self) - if packet_sink._hb.output_signature().sizeof_stream_item(0): - self.connect(packet_sink, - blocks.null_sink(packet_sink._hb.output_signature().sizeof_stream_item(0))) - - def recv_pkt(self, ok, payload): - msg = blocks.message_from_string(payload, 0, self._item_size_out, - len(payload)/self._item_size_out) - if ok: self._msgq_out.insert_tail(msg) + """ + Hierarchical block for wrapping packet sink block. + """ + + def __init__(self, packet_sink=None): + #initialize hier2 + gr.hier_block2.__init__( + self, + "ofdm_mod", + gr.io_signature(1, 1, packet_sink._hb.input_signature().sizeof_stream_item(0)), # Input signature + gr.io_signature(1, 1, self._item_size_out) # Output signature + ) + #create blocks + msg_source = blocks.message_source(self._item_size_out, DEFAULT_MSGQ_LIMIT) + self._msgq_out = msg_source.msgq() + #connect + self.connect(self, packet_sink) + self.connect(msg_source, self) + if packet_sink._hb.output_signature().sizeof_stream_item(0): + self.connect(packet_sink, + blocks.null_sink(packet_sink._hb.output_signature().sizeof_stream_item(0))) + + def recv_pkt(self, ok, payload): + msg = gr.message_from_string(payload, 0, self._item_size_out, + len(payload)/self._item_size_out) + if ok: self._msgq_out.insert_tail(msg) class packet_demod_b(packet_demod_base): _item_size_out = gr.sizeof_char class packet_demod_s(packet_demod_base): _item_size_out = gr.sizeof_short diff --git a/grc/grc_gnuradio/blks2/selector.py b/grc/grc_gnuradio/blks2/selector.py index bc393f84b6..7ae664823b 100644 --- a/grc/grc_gnuradio/blks2/selector.py +++ b/grc/grc_gnuradio/blks2/selector.py @@ -23,120 +23,120 @@ from gnuradio import gr from gnuradio import blocks class selector(gr.hier_block2): - """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m.""" - def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index): - """ - Selector constructor. - - Args: - item_size: the size of the gr data stream in bytes - num_inputs: the number of inputs (integer) - num_outputs: the number of outputs (integer) - input_index: the index for the source data - output_index: the index for the destination data - """ - gr.hier_block2.__init__( - self, 'selector', - gr.io_signature(num_inputs, num_inputs, item_size), - gr.io_signature(num_outputs, num_outputs, item_size), - ) - #terminator blocks for unused inputs and outputs - self.input_terminators = [blocks.null_sink(item_size) for i in range(num_inputs)] - self.output_terminators = [blocks.head(item_size, 0) for i in range(num_outputs)] - self.copy = blocks.copy(item_size) - #connections - for i in range(num_inputs): self.connect((self, i), self.input_terminators[i]) - for i in range(num_outputs): self.connect(blocks.null_source(item_size), + """A hier2 block with N inputs and M outputs, where data is only forwarded through input n to output m.""" + def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index): + """ + Selector constructor. + + Args: + item_size: the size of the gr data stream in bytes + num_inputs: the number of inputs (integer) + num_outputs: the number of outputs (integer) + input_index: the index for the source data + output_index: the index for the destination data + """ + gr.hier_block2.__init__( + self, 'selector', + gr.io_signature(num_inputs, num_inputs, item_size), + gr.io_signature(num_outputs, num_outputs, item_size), + ) + #terminator blocks for unused inputs and outputs + self.input_terminators = [blocks.null_sink(item_size) for i in range(num_inputs)] + self.output_terminators = [blocks.head(item_size, 0) for i in range(num_outputs)] + self.copy = blocks.copy(item_size) + #connections + for i in range(num_inputs): self.connect((self, i), self.input_terminators[i]) + for i in range(num_outputs): self.connect(blocks.null_source(item_size), self.output_terminators[i], (self, i)) - self.item_size = item_size - self.input_index = input_index - self.output_index = output_index - self.num_inputs = num_inputs - self.num_outputs = num_outputs - self._connect_current() + self.item_size = item_size + self.input_index = input_index + self.output_index = output_index + self.num_inputs = num_inputs + self.num_outputs = num_outputs + self._connect_current() - def _indexes_valid(self): - """ - Are the input and output indexes within range of the number of inputs and outputs? - - Returns: - true if input index and output index are in range - """ - return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs) + def _indexes_valid(self): + """ + Are the input and output indexes within range of the number of inputs and outputs? + + Returns: + true if input index and output index are in range + """ + return self.input_index in range(self.num_inputs) and self.output_index in range(self.num_outputs) - def _connect_current(self): - """If the input and output indexes are valid: - disconnect the blocks at the input and output index from their terminators, - and connect them to one another. Then connect the terminators to one another.""" - if self._indexes_valid(): - self.disconnect((self, self.input_index), self.input_terminators[self.input_index]) - self.disconnect(self.output_terminators[self.output_index], (self, self.output_index)) - self.connect((self, self.input_index), self.copy) - self.connect(self.copy, (self, self.output_index)) - self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) + def _connect_current(self): + """If the input and output indexes are valid: + disconnect the blocks at the input and output index from their terminators, + and connect them to one another. Then connect the terminators to one another.""" + if self._indexes_valid(): + self.disconnect((self, self.input_index), self.input_terminators[self.input_index]) + self.disconnect(self.output_terminators[self.output_index], (self, self.output_index)) + self.connect((self, self.input_index), self.copy) + self.connect(self.copy, (self, self.output_index)) + self.connect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) - def _disconnect_current(self): - """If the input and output indexes are valid: - disconnect the blocks at the input and output index from one another, - and the terminators at the input and output index from one another. - Reconnect the blocks to the terminators.""" - if self._indexes_valid(): - self.disconnect((self, self.input_index), self.copy) - self.disconnect(self.copy, (self, self.output_index)) - self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) - self.connect((self, self.input_index), self.input_terminators[self.input_index]) - self.connect(self.output_terminators[self.output_index], (self, self.output_index)) + def _disconnect_current(self): + """If the input and output indexes are valid: + disconnect the blocks at the input and output index from one another, + and the terminators at the input and output index from one another. + Reconnect the blocks to the terminators.""" + if self._indexes_valid(): + self.disconnect((self, self.input_index), self.copy) + self.disconnect(self.copy, (self, self.output_index)) + self.disconnect(self.output_terminators[self.output_index], self.input_terminators[self.input_index]) + self.connect((self, self.input_index), self.input_terminators[self.input_index]) + self.connect(self.output_terminators[self.output_index], (self, self.output_index)) - def set_input_index(self, input_index): - """ - Change the block to the new input index if the index changed. - - Args: - input_index: the new input index - """ - if self.input_index != input_index: - self.lock() - self._disconnect_current() - self.input_index = input_index - self._connect_current() - self.unlock() + def set_input_index(self, input_index): + """ + Change the block to the new input index if the index changed. + + Args: + input_index: the new input index + """ + if self.input_index != input_index: + self.lock() + self._disconnect_current() + self.input_index = input_index + self._connect_current() + self.unlock() - def set_output_index(self, output_index): - """ - Change the block to the new output index if the index changed. - - Args: - output_index: the new output index - """ - if self.output_index != output_index: - self.lock() - self._disconnect_current() - self.output_index = output_index - self._connect_current() - self.unlock() + def set_output_index(self, output_index): + """ + Change the block to the new output index if the index changed. + + Args: + output_index: the new output index + """ + if self.output_index != output_index: + self.lock() + self._disconnect_current() + self.output_index = output_index + self._connect_current() + self.unlock() class valve(selector): - """Wrapper for selector with 1 input and 1 output.""" + """Wrapper for selector with 1 input and 1 output.""" - def __init__(self, item_size, open): - """ - Constructor for valve. - - Args: - item_size: the size of the gr data stream in bytes - open: true if initial valve state is open - """ - if open: output_index = -1 - else: output_index = 0 - selector.__init__(self, item_size, 1, 1, 0, output_index) + def __init__(self, item_size, open): + """ + Constructor for valve. + + Args: + item_size: the size of the gr data stream in bytes + open: true if initial valve state is open + """ + if open: output_index = -1 + else: output_index = 0 + selector.__init__(self, item_size, 1, 1, 0, output_index) - def set_open(self, open): - """ - Callback to set open state. - - Args: - open: true to set valve state to open - """ - if open: output_index = -1 - else: output_index = 0 - self.set_output_index(output_index) + def set_open(self, open): + """ + Callback to set open state. + + Args: + open: true to set valve state to open + """ + if open: output_index = -1 + else: output_index = 0 + self.set_output_index(output_index) diff --git a/grc/grc_gnuradio/blks2/tcp.py b/grc/grc_gnuradio/blks2/tcp.py index 33160020cb..8613c02a17 100644 --- a/grc/grc_gnuradio/blks2/tcp.py +++ b/grc/grc_gnuradio/blks2/tcp.py @@ -24,47 +24,47 @@ import socket import os def _get_sock_fd(addr, port, server): - """ - Get the file descriptor for the socket. - As a client, block on connect, dup the socket descriptor. - As a server, block on accept, dup the client descriptor. - - Args: - addr: the ip address string - port: the tcp port number - server: true for server mode, false for client mode - - Returns: - the file descriptor number - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if server: - sock.bind((addr, port)) - sock.listen(1) - clientsock, address = sock.accept() - return os.dup(clientsock.fileno()) - else: - sock.connect((addr, port)) - return os.dup(sock.fileno()) + """ + Get the file descriptor for the socket. + As a client, block on connect, dup the socket descriptor. + As a server, block on accept, dup the client descriptor. + + Args: + addr: the ip address string + port: the tcp port number + server: true for server mode, false for client mode + + Returns: + the file descriptor number + """ + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + if server: + sock.bind((addr, port)) + sock.listen(1) + clientsock, address = sock.accept() + return os.dup(clientsock.fileno()) + else: + sock.connect((addr, port)) + return os.dup(sock.fileno()) class tcp_source(gr.hier_block2): - def __init__(self, itemsize, addr, port, server=True): - #init hier block - gr.hier_block2.__init__( - self, 'tcp_source', - gr.io_signature(0, 0, 0), - gr.io_signature(1, 1, itemsize), - ) - fd = _get_sock_fd(addr, port, server) - self.connect(blocks.file_descriptor_source(itemsize, fd), self) + def __init__(self, itemsize, addr, port, server=True): + #init hier block + gr.hier_block2.__init__( + self, 'tcp_source', + gr.io_signature(0, 0, 0), + gr.io_signature(1, 1, itemsize), + ) + fd = _get_sock_fd(addr, port, server) + self.connect(blocks.file_descriptor_source(itemsize, fd), self) class tcp_sink(gr.hier_block2): - def __init__(self, itemsize, addr, port, server=False): - #init hier block - gr.hier_block2.__init__( - self, 'tcp_sink', - gr.io_signature(1, 1, itemsize), - gr.io_signature(0, 0, 0), - ) - fd = _get_sock_fd(addr, port, server) - self.connect(self, blocks.file_descriptor_sink(itemsize, fd)) + def __init__(self, itemsize, addr, port, server=False): + #init hier block + gr.hier_block2.__init__( + self, 'tcp_sink', + gr.io_signature(1, 1, itemsize), + gr.io_signature(0, 0, 0), + ) + fd = _get_sock_fd(addr, port, server) + self.connect(self, blocks.file_descriptor_sink(itemsize, fd)) diff --git a/grc/gui/ActionHandler.py b/grc/gui/ActionHandler.py index add32dbdac..71d62fc8f5 100644 --- a/grc/gui/ActionHandler.py +++ b/grc/gui/ActionHandler.py @@ -37,132 +37,132 @@ from FileDialogs import OpenFlowGraphFileDialog, SaveFlowGraphFileDialog, SaveIm gobject.threads_init() class ActionHandler: - """ - The action handler will setup all the major window components, - and handle button presses and flow graph operations from the GUI. - """ - - def __init__(self, file_paths, platform): - """ - ActionHandler constructor. - Create the main window, setup the message handler, import the preferences, - and connect all of the action handlers. Finally, enter the gtk main loop and block. - - Args: - file_paths: a list of flow graph file passed from command line - platform: platform module - """ - self.clipboard = None - for action in Actions.get_all_actions(): action.connect('activate', self._handle_action) - #setup the main window - self.platform = platform; - self.main_window = MainWindow(platform) - self.main_window.connect('delete-event', self._quit) - self.main_window.connect('key-press-event', self._handle_key_press) - self.get_page = self.main_window.get_page - self.get_flow_graph = self.main_window.get_flow_graph - self.get_focus_flag = self.main_window.get_focus_flag - #setup the messages - Messages.register_messenger(self.main_window.add_report_line) - Messages.send_init(platform) - #initialize - self.init_file_paths = file_paths - Actions.APPLICATION_INITIALIZE() - #enter the mainloop - gtk.main() - - def _handle_key_press(self, widget, event): - """ - Handle key presses from the keyboard and translate key combinations into actions. - This key press handler is called prior to the gtk key press handler. - This handler bypasses built in accelerator key handling when in focus because - * some keys are ignored by the accelerators like the direction keys, - * some keys are not registered to any accelerators but are still used. - When not in focus, gtk and the accelerators handle the the key press. - - Returns: - false to let gtk handle the key action - """ - if not self.get_focus_flag(): return False - return Actions.handle_key_press(event) - - def _quit(self, window, event): - """ - Handle the delete event from the main window. - Generated by pressing X to close, alt+f4, or right click+close. - This method in turns calls the state handler to quit. - - Returns: - true - """ - Actions.APPLICATION_QUIT() - return True - - def _handle_action(self, action): - #print action - ################################################## - # Initalize/Quit - ################################################## - if action == Actions.APPLICATION_INITIALIZE: - for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled - #enable a select few actions - for action in ( - Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW, - Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS, - Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, - Actions.TYPES_WINDOW_DISPLAY, - ): action.set_sensitive(True) - if not self.init_file_paths: - self.init_file_paths = Preferences.files_open() - if not self.init_file_paths: self.init_file_paths = [''] - for file_path in self.init_file_paths: - if file_path: self.main_window.new_page(file_path) #load pages from file paths - if Preferences.file_open() in self.init_file_paths: - self.main_window.new_page(Preferences.file_open(), show=True) - if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists - elif action == Actions.APPLICATION_QUIT: - if self.main_window.close_pages(): - gtk.main_quit() - exit(0) - ################################################## - # Selections - ################################################## - elif action == Actions.ELEMENT_SELECT: - pass #do nothing, update routines below - elif action == Actions.NOTHING_SELECT: - self.get_flow_graph().unselect() - ################################################## - # Enable/Disable - ################################################## - elif action == Actions.BLOCK_ENABLE: - if self.get_flow_graph().enable_selected(True): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_DISABLE: - if self.get_flow_graph().enable_selected(False): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - ################################################## - # Cut/Copy/Paste - ################################################## - elif action == Actions.BLOCK_CUT: - Actions.BLOCK_COPY() - Actions.ELEMENT_DELETE() - elif action == Actions.BLOCK_COPY: - self.clipboard = self.get_flow_graph().copy_to_clipboard() - elif action == Actions.BLOCK_PASTE: - if self.clipboard: - self.get_flow_graph().paste_from_clipboard(self.clipboard) - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) + """ + The action handler will setup all the major window components, + and handle button presses and flow graph operations from the GUI. + """ + + def __init__(self, file_paths, platform): + """ + ActionHandler constructor. + Create the main window, setup the message handler, import the preferences, + and connect all of the action handlers. Finally, enter the gtk main loop and block. + + Args: + file_paths: a list of flow graph file passed from command line + platform: platform module + """ + self.clipboard = None + for action in Actions.get_all_actions(): action.connect('activate', self._handle_action) + #setup the main window + self.platform = platform; + self.main_window = MainWindow(platform) + self.main_window.connect('delete-event', self._quit) + self.main_window.connect('key-press-event', self._handle_key_press) + self.get_page = self.main_window.get_page + self.get_flow_graph = self.main_window.get_flow_graph + self.get_focus_flag = self.main_window.get_focus_flag + #setup the messages + Messages.register_messenger(self.main_window.add_report_line) + Messages.send_init(platform) + #initialize + self.init_file_paths = file_paths + Actions.APPLICATION_INITIALIZE() + #enter the mainloop + gtk.main() + + def _handle_key_press(self, widget, event): + """ + Handle key presses from the keyboard and translate key combinations into actions. + This key press handler is called prior to the gtk key press handler. + This handler bypasses built in accelerator key handling when in focus because + * some keys are ignored by the accelerators like the direction keys, + * some keys are not registered to any accelerators but are still used. + When not in focus, gtk and the accelerators handle the the key press. + + Returns: + false to let gtk handle the key action + """ + if not self.get_focus_flag(): return False + return Actions.handle_key_press(event) + + def _quit(self, window, event): + """ + Handle the delete event from the main window. + Generated by pressing X to close, alt+f4, or right click+close. + This method in turns calls the state handler to quit. + + Returns: + true + """ + Actions.APPLICATION_QUIT() + return True + + def _handle_action(self, action): + #print action + ################################################## + # Initalize/Quit + ################################################## + if action == Actions.APPLICATION_INITIALIZE: + for action in Actions.get_all_actions(): action.set_sensitive(False) #set all actions disabled + #enable a select few actions + for action in ( + Actions.APPLICATION_QUIT, Actions.FLOW_GRAPH_NEW, + Actions.FLOW_GRAPH_OPEN, Actions.FLOW_GRAPH_SAVE_AS, + Actions.FLOW_GRAPH_CLOSE, Actions.ABOUT_WINDOW_DISPLAY, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, Actions.HELP_WINDOW_DISPLAY, + Actions.TYPES_WINDOW_DISPLAY, + ): action.set_sensitive(True) + if not self.init_file_paths: + self.init_file_paths = Preferences.files_open() + if not self.init_file_paths: self.init_file_paths = [''] + for file_path in self.init_file_paths: + if file_path: self.main_window.new_page(file_path) #load pages from file paths + if Preferences.file_open() in self.init_file_paths: + self.main_window.new_page(Preferences.file_open(), show=True) + if not self.get_page(): self.main_window.new_page() #ensure that at least a blank page exists + elif action == Actions.APPLICATION_QUIT: + if self.main_window.close_pages(): + gtk.main_quit() + exit(0) + ################################################## + # Selections + ################################################## + elif action == Actions.ELEMENT_SELECT: + pass #do nothing, update routines below + elif action == Actions.NOTHING_SELECT: + self.get_flow_graph().unselect() + ################################################## + # Enable/Disable + ################################################## + elif action == Actions.BLOCK_ENABLE: + if self.get_flow_graph().enable_selected(True): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_DISABLE: + if self.get_flow_graph().enable_selected(False): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + ################################################## + # Cut/Copy/Paste + ################################################## + elif action == Actions.BLOCK_CUT: + Actions.BLOCK_COPY() + Actions.ELEMENT_DELETE() + elif action == Actions.BLOCK_COPY: + self.clipboard = self.get_flow_graph().copy_to_clipboard() + elif action == Actions.BLOCK_PASTE: + if self.clipboard: + self.get_flow_graph().paste_from_clipboard(self.clipboard) + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) ################################################## # Create heir block ################################################## - elif action == Actions.BLOCK_CREATE_HIER: + elif action == Actions.BLOCK_CREATE_HIER: # keeping track of coordinates for pasting later coords = self.get_flow_graph().get_selected_blocks()[0].get_coordinate() @@ -211,8 +211,8 @@ class ActionHandler: # Copy the selected blocks and paste them into a new page # then move the flowgraph to a reasonable position - Actions.BLOCK_COPY() - self.main_window.new_page() + Actions.BLOCK_COPY() + self.main_window.new_page() Actions.BLOCK_PASTE() coords = (x_min,y_min) self.get_flow_graph().move_selected(coords) @@ -284,255 +284,272 @@ class ActionHandler: new_connection = self.get_flow_graph().connect(pad_source,sink) # update the new heir block flow graph - self.get_flow_graph().update() + self.get_flow_graph().update() - ################################################## - # Move/Rotate/Delete/Create - ################################################## - elif action == Actions.BLOCK_MOVE: - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_ROTATE_CCW: - if self.get_flow_graph().rotate_selected(90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_ROTATE_CW: - if self.get_flow_graph().rotate_selected(-90): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.ELEMENT_DELETE: - if self.get_flow_graph().remove_selected(): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - Actions.NOTHING_SELECT() - self.get_page().set_saved(False) - elif action == Actions.ELEMENT_CREATE: - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - Actions.NOTHING_SELECT() - self.get_page().set_saved(False) - elif action == Actions.BLOCK_INC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.BLOCK_DEC_TYPE: - if self.get_flow_graph().type_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.PORT_CONTROLLER_INC: - if self.get_flow_graph().port_controller_modify_selected(1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - elif action == Actions.PORT_CONTROLLER_DEC: - if self.get_flow_graph().port_controller_modify_selected(-1): - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - ################################################## - # Window stuff - ################################################## - elif action == Actions.ABOUT_WINDOW_DISPLAY: - Dialogs.AboutDialog(self.get_flow_graph().get_parent()) - elif action == Actions.HELP_WINDOW_DISPLAY: - Dialogs.HelpDialog() - elif action == Actions.TYPES_WINDOW_DISPLAY: - Dialogs.TypesDialog(self.get_flow_graph().get_parent()) - elif action == Actions.ERRORS_WINDOW_DISPLAY: - Dialogs.ErrorsDialog(self.get_flow_graph()) - ################################################## - # Param Modifications - ################################################## - elif action == Actions.BLOCK_PARAM_MODIFY: - selected_block = self.get_flow_graph().get_selected_block() - if selected_block: - if PropsDialog(selected_block).run(): - #save the new state - self.get_flow_graph().update() - self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) - self.get_page().set_saved(False) - else: - #restore the current state - n = self.get_page().get_state_cache().get_current_state() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - ################################################## - # Undo/Redo - ################################################## - elif action == Actions.FLOW_GRAPH_UNDO: - n = self.get_page().get_state_cache().get_prev_state() - if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) - elif action == Actions.FLOW_GRAPH_REDO: - n = self.get_page().get_state_cache().get_next_state() - if n: - self.get_flow_graph().unselect() - self.get_flow_graph().import_data(n) - self.get_flow_graph().update() - self.get_page().set_saved(False) - ################################################## - # New/Open/Save/Close - ################################################## - elif action == Actions.FLOW_GRAPH_NEW: - self.main_window.new_page() - elif action == Actions.FLOW_GRAPH_OPEN: - file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run() - if file_paths: #open a new page for each file, show only the first - for i,file_path in enumerate(file_paths): - self.main_window.new_page(file_path, show=(i==0)) - elif action == Actions.FLOW_GRAPH_CLOSE: - self.main_window.close_page() - elif action == Actions.FLOW_GRAPH_SAVE: - #read-only or undefined file path, do save-as - if self.get_page().get_read_only() or not self.get_page().get_file_path(): - Actions.FLOW_GRAPH_SAVE_AS() - #otherwise try to save - else: - try: - ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path()) - self.get_flow_graph().grc_file_path = self.get_page().get_file_path() - self.get_page().set_saved(True) - except IOError: - Messages.send_fail_save(self.get_page().get_file_path()) - self.get_page().set_saved(False) - elif action == Actions.FLOW_GRAPH_SAVE_AS: - file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() - if file_path is not None: - self.get_page().set_file_path(file_path) - Actions.FLOW_GRAPH_SAVE() - elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: - file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() - if file_path is not None: - pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() - pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:]) - ################################################## - # Gen/Exec/Stop - ################################################## - elif action == Actions.FLOW_GRAPH_GEN: - if not self.get_page().get_proc(): - if not self.get_page().get_saved() or not self.get_page().get_file_path(): - Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved - if self.get_page().get_saved() and self.get_page().get_file_path(): - generator = self.get_page().get_generator() - try: - Messages.send_start_gen(generator.get_file_path()) - generator.write() - except Exception,e: Messages.send_fail_gen(e) - else: self.generator = None - elif action == Actions.FLOW_GRAPH_EXEC: - if not self.get_page().get_proc(): - Actions.FLOW_GRAPH_GEN() - if self.get_page().get_saved() and self.get_page().get_file_path(): - ExecFlowGraphThread(self) - elif action == Actions.FLOW_GRAPH_KILL: - if self.get_page().get_proc(): - try: self.get_page().get_proc().kill() - except: print "could not kill process: %d"%self.get_page().get_proc().pid - elif action == Actions.PAGE_CHANGE: #pass and run the global actions - pass - elif action == Actions.RELOAD_BLOCKS: - self.platform.loadblocks() - self.main_window.btwin.clear(); - self.platform.load_block_tree(self.main_window.btwin); - elif action == Actions.OPEN_HIER: - bn = []; - for b in self.get_flow_graph().get_selected_blocks(): - if b._grc_source: - self.main_window.new_page(b._grc_source, show=True); - else: print '!!! Action "%s" not handled !!!'%action - ################################################## - # Global Actions for all States - ################################################## - #update general buttons - Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) - Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) - Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) - Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - #update cut/copy/paste - Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) - #update enable/disable - Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) - Actions.RELOAD_BLOCKS.set_sensitive(True) - #set the exec and stop buttons - self.update_exec_stop() - #saved status - Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) - self.main_window.update() - try: #set the size of the flow graph area (if changed) - new_size = self.get_flow_graph().get_option('window_size') - if self.get_flow_graph().get_size() != tuple(new_size): - self.get_flow_graph().set_size(*new_size) - except: pass - #draw the flow graph - self.get_flow_graph().update_selected() - self.get_flow_graph().queue_draw() - return True #action was handled - - def update_exec_stop(self): - """ - Update the exec and stop buttons. - Lock and unlock the mutex for race conditions with exec flow graph threads. - """ - sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc() - Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive) - Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive) - Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() != None) + ################################################## + # Move/Rotate/Delete/Create + ################################################## + elif action == Actions.BLOCK_MOVE: + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_ROTATE_CCW: + if self.get_flow_graph().rotate_selected(90): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_ROTATE_CW: + if self.get_flow_graph().rotate_selected(-90): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.ELEMENT_DELETE: + if self.get_flow_graph().remove_selected(): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + Actions.NOTHING_SELECT() + self.get_page().set_saved(False) + elif action == Actions.ELEMENT_CREATE: + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + Actions.NOTHING_SELECT() + self.get_page().set_saved(False) + elif action == Actions.BLOCK_INC_TYPE: + if self.get_flow_graph().type_controller_modify_selected(1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.BLOCK_DEC_TYPE: + if self.get_flow_graph().type_controller_modify_selected(-1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.PORT_CONTROLLER_INC: + if self.get_flow_graph().port_controller_modify_selected(1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + elif action == Actions.PORT_CONTROLLER_DEC: + if self.get_flow_graph().port_controller_modify_selected(-1): + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + ################################################## + # Window stuff + ################################################## + elif action == Actions.ABOUT_WINDOW_DISPLAY: + Dialogs.AboutDialog(self.get_flow_graph().get_parent()) + elif action == Actions.HELP_WINDOW_DISPLAY: + Dialogs.HelpDialog() + elif action == Actions.TYPES_WINDOW_DISPLAY: + Dialogs.TypesDialog(self.get_flow_graph().get_parent()) + elif action == Actions.ERRORS_WINDOW_DISPLAY: + Dialogs.ErrorsDialog(self.get_flow_graph()) + ################################################## + # Param Modifications + ################################################## + elif action == Actions.BLOCK_PARAM_MODIFY: + selected_block = self.get_flow_graph().get_selected_block() + if selected_block: + if PropsDialog(selected_block).run(): + #save the new state + self.get_flow_graph().update() + self.get_page().get_state_cache().save_new_state(self.get_flow_graph().export_data()) + self.get_page().set_saved(False) + else: + #restore the current state + n = self.get_page().get_state_cache().get_current_state() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + ################################################## + # Undo/Redo + ################################################## + elif action == Actions.FLOW_GRAPH_UNDO: + n = self.get_page().get_state_cache().get_prev_state() + if n: + self.get_flow_graph().unselect() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + self.get_page().set_saved(False) + elif action == Actions.FLOW_GRAPH_REDO: + n = self.get_page().get_state_cache().get_next_state() + if n: + self.get_flow_graph().unselect() + self.get_flow_graph().import_data(n) + self.get_flow_graph().update() + self.get_page().set_saved(False) + ################################################## + # New/Open/Save/Close + ################################################## + elif action == Actions.FLOW_GRAPH_NEW: + self.main_window.new_page() + elif action == Actions.FLOW_GRAPH_OPEN: + file_paths = OpenFlowGraphFileDialog(self.get_page().get_file_path()).run() + if file_paths: #open a new page for each file, show only the first + for i,file_path in enumerate(file_paths): + self.main_window.new_page(file_path, show=(i==0)) + elif action == Actions.FLOW_GRAPH_CLOSE: + self.main_window.close_page() + elif action == Actions.FLOW_GRAPH_SAVE: + #read-only or undefined file path, do save-as + if self.get_page().get_read_only() or not self.get_page().get_file_path(): + Actions.FLOW_GRAPH_SAVE_AS() + #otherwise try to save + else: + try: + ParseXML.to_file(self.get_flow_graph().export_data(), self.get_page().get_file_path()) + self.get_flow_graph().grc_file_path = self.get_page().get_file_path() + self.get_page().set_saved(True) + except IOError: + Messages.send_fail_save(self.get_page().get_file_path()) + self.get_page().set_saved(False) + elif action == Actions.FLOW_GRAPH_SAVE_AS: + file_path = SaveFlowGraphFileDialog(self.get_page().get_file_path()).run() + if file_path is not None: + self.get_page().set_file_path(file_path) + Actions.FLOW_GRAPH_SAVE() + elif action == Actions.FLOW_GRAPH_SCREEN_CAPTURE: + file_path = SaveImageFileDialog(self.get_page().get_file_path()).run() + if file_path is not None: + pixbuf = self.get_flow_graph().get_drawing_area().get_pixbuf() + pixbuf.save(file_path, IMAGE_FILE_EXTENSION[1:]) + ################################################## + # Gen/Exec/Stop + ################################################## + elif action == Actions.FLOW_GRAPH_GEN: + if not self.get_page().get_proc(): + if not self.get_page().get_saved() or not self.get_page().get_file_path(): + Actions.FLOW_GRAPH_SAVE() #only save if file path missing or not saved + if self.get_page().get_saved() and self.get_page().get_file_path(): + generator = self.get_page().get_generator() + try: + Messages.send_start_gen(generator.get_file_path()) + generator.write() + except Exception,e: Messages.send_fail_gen(e) + else: self.generator = None + elif action == Actions.FLOW_GRAPH_EXEC: + if not self.get_page().get_proc(): + Actions.FLOW_GRAPH_GEN() + if self.get_page().get_saved() and self.get_page().get_file_path(): + ExecFlowGraphThread(self) + elif action == Actions.FLOW_GRAPH_KILL: + if self.get_page().get_proc(): + try: self.get_page().get_proc().kill() + except: print "could not kill process: %d"%self.get_page().get_proc().pid + elif action == Actions.PAGE_CHANGE: #pass and run the global actions + pass + elif action == Actions.RELOAD_BLOCKS: + self.platform.loadblocks() + self.main_window.btwin.clear(); + self.platform.load_block_tree(self.main_window.btwin); + elif action == Actions.OPEN_HIER: + bn = []; + for b in self.get_flow_graph().get_selected_blocks(): + if b._grc_source: + self.main_window.new_page(b._grc_source, show=True); + elif action == Actions.BUSSIFY_SOURCES: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'source'); + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + + elif action == Actions.BUSSIFY_SINKS: + n = {'name':'bus', 'type':'bus'} + for b in self.get_flow_graph().get_selected_blocks(): + b.bussify(n, 'sink') + self.get_flow_graph()._old_selected_port = None; + self.get_flow_graph()._new_selected_port = None; + Actions.ELEMENT_CREATE(); + else: print '!!! Action "%s" not handled !!!'%action + ################################################## + # Global Actions for all States + ################################################## + #update general buttons + Actions.ERRORS_WINDOW_DISPLAY.set_sensitive(not self.get_flow_graph().is_valid()) + Actions.ELEMENT_DELETE.set_sensitive(bool(self.get_flow_graph().get_selected_elements())) + Actions.BLOCK_PARAM_MODIFY.set_sensitive(bool(self.get_flow_graph().get_selected_block())) + Actions.BLOCK_ROTATE_CCW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_ROTATE_CW.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + #update cut/copy/paste + Actions.BLOCK_CUT.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_COPY.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_PASTE.set_sensitive(bool(self.clipboard)) + #update enable/disable + Actions.BLOCK_ENABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_DISABLE.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BLOCK_CREATE_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.OPEN_HIER.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BUSSIFY_SOURCES.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.BUSSIFY_SINKS.set_sensitive(bool(self.get_flow_graph().get_selected_blocks())) + Actions.RELOAD_BLOCKS.set_sensitive(True) + #set the exec and stop buttons + self.update_exec_stop() + #saved status + Actions.FLOW_GRAPH_SAVE.set_sensitive(not self.get_page().get_saved()) + self.main_window.update() + try: #set the size of the flow graph area (if changed) + new_size = self.get_flow_graph().get_option('window_size') + if self.get_flow_graph().get_size() != tuple(new_size): + self.get_flow_graph().set_size(*new_size) + except: pass + #draw the flow graph + self.get_flow_graph().update_selected() + self.get_flow_graph().queue_draw() + return True #action was handled + + def update_exec_stop(self): + """ + Update the exec and stop buttons. + Lock and unlock the mutex for race conditions with exec flow graph threads. + """ + sensitive = self.get_flow_graph().is_valid() and not self.get_page().get_proc() + Actions.FLOW_GRAPH_GEN.set_sensitive(sensitive) + Actions.FLOW_GRAPH_EXEC.set_sensitive(sensitive) + Actions.FLOW_GRAPH_KILL.set_sensitive(self.get_page().get_proc() != None) class ExecFlowGraphThread(Thread): - """Execute the flow graph as a new process and wait on it to finish.""" - - def __init__ (self, action_handler): - """ - ExecFlowGraphThread constructor. - - Args: - action_handler: an instance of an ActionHandler - """ - Thread.__init__(self) - self.update_exec_stop = action_handler.update_exec_stop - self.flow_graph = action_handler.get_flow_graph() - #store page and dont use main window calls in run - self.page = action_handler.get_page() - Messages.send_start_exec(self.page.get_generator().get_file_path()) - #get the popen - try: - self.p = self.page.get_generator().get_popen() - self.page.set_proc(self.p) - #update - self.update_exec_stop() - self.start() - except Exception, e: - Messages.send_verbose_exec(str(e)) - Messages.send_end_exec() - - def run(self): - """ - Wait on the executing process by reading from its stdout. - Use gobject.idle_add when calling functions that modify gtk objects. - """ - #handle completion - r = "\n" - while(r): - gobject.idle_add(Messages.send_verbose_exec, r) - r = os.read(self.p.stdout.fileno(), 1024) - gobject.idle_add(self.done) - - def done(self): - """Perform end of execution tasks.""" - Messages.send_end_exec() - self.page.set_proc(None) - self.update_exec_stop() + """Execute the flow graph as a new process and wait on it to finish.""" + + def __init__ (self, action_handler): + """ + ExecFlowGraphThread constructor. + + Args: + action_handler: an instance of an ActionHandler + """ + Thread.__init__(self) + self.update_exec_stop = action_handler.update_exec_stop + self.flow_graph = action_handler.get_flow_graph() + #store page and dont use main window calls in run + self.page = action_handler.get_page() + Messages.send_start_exec(self.page.get_generator().get_file_path()) + #get the popen + try: + self.p = self.page.get_generator().get_popen() + self.page.set_proc(self.p) + #update + self.update_exec_stop() + self.start() + except Exception, e: + Messages.send_verbose_exec(str(e)) + Messages.send_end_exec() + + def run(self): + """ + Wait on the executing process by reading from its stdout. + Use gobject.idle_add when calling functions that modify gtk objects. + """ + #handle completion + r = "\n" + while(r): + gobject.idle_add(Messages.send_verbose_exec, r) + r = os.read(self.p.stdout.fileno(), 1024) + gobject.idle_add(self.done) + + def done(self): + """Perform end of execution tasks.""" + Messages.send_end_exec() + self.page.set_proc(None) + self.update_exec_stop() diff --git a/grc/gui/Actions.py b/grc/gui/Actions.py index 9225b0bd52..a70109c021 100644 --- a/grc/gui/Actions.py +++ b/grc/gui/Actions.py @@ -30,26 +30,26 @@ _actions_keypress_dict = dict() _keymap = gtk.gdk.keymap_get_default() _used_mods_mask = NO_MODS_MASK def handle_key_press(event): - """ - Call the action associated with the key press event. - Both the key value and the mask must have a match. - - Args: - event: a gtk key press event - - Returns: - true if handled - """ - _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK) - #extract the key value and the consumed modifiers - keyval, egroup, level, consumed = _keymap.translate_keyboard_state( - event.hardware_keycode, event.state, event.group) - #get the modifier mask and ignore irrelevant modifiers - mod_mask = event.state & ~consumed & _used_mods_mask - #look up the keypress and call the action - try: _actions_keypress_dict[(keyval, mod_mask)]() - except KeyError: return False #not handled - return True #handled here + """ + Call the action associated with the key press event. + Both the key value and the mask must have a match. + + Args: + event: a gtk key press event + + Returns: + true if handled + """ + _used_mods_mask = reduce(lambda x, y: x | y, [mod_mask for keyval, mod_mask in _actions_keypress_dict], NO_MODS_MASK) + #extract the key value and the consumed modifiers + keyval, egroup, level, consumed = _keymap.translate_keyboard_state( + event.hardware_keycode, event.state, event.group) + #get the modifier mask and ignore irrelevant modifiers + mod_mask = event.state & ~consumed & _used_mods_mask + #look up the keypress and call the action + try: _actions_keypress_dict[(keyval, mod_mask)]() + except KeyError: return False #not handled + return True #handled here _all_actions_list = list() def get_all_actions(): return _all_actions_list @@ -58,240 +58,250 @@ _accel_group = gtk.AccelGroup() def get_accel_group(): return _accel_group class Action(gtk.Action): - """ - A custom Action class based on gtk.Action. - Pass additional arguments such as keypresses. - Register actions and keypresses with this module. - """ + """ + A custom Action class based on gtk.Action. + Pass additional arguments such as keypresses. + Register actions and keypresses with this module. + """ - def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): - """ - Create a new Action instance. - - Args: - key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) - the: regular gtk.Action parameters (defaults to None) - """ - if name is None: name = label - gtk.Action.__init__(self, - name=name, label=label, - tooltip=tooltip, stock_id=stock_id, - ) - #register this action - _all_actions_list.append(self) - for i in range(len(keypresses)/2): - keyval, mod_mask = keypresses[i*2:(i+1)*2] - #register this keypress - if _actions_keypress_dict.has_key((keyval, mod_mask)): - raise KeyError('keyval/mod_mask pair already registered "%s"'%str((keyval, mod_mask))) - _actions_keypress_dict[(keyval, mod_mask)] = self - #set the accelerator group, and accelerator path - #register the key name and mod mask with the accelerator path - if label is None: continue #dont register accel - accel_path = '<main>/'+self.get_name() - self.set_accel_group(get_accel_group()) - self.set_accel_path(accel_path) - gtk.accel_map_add_entry(accel_path, keyval, mod_mask) + def __init__(self, keypresses=(), name=None, label=None, tooltip=None, stock_id=None): + """ + Create a new Action instance. + + Args: + key_presses: a tuple of (keyval1, mod_mask1, keyval2, mod_mask2, ...) + the: regular gtk.Action parameters (defaults to None) + """ + if name is None: name = label + gtk.Action.__init__(self, + name=name, label=label, + tooltip=tooltip, stock_id=stock_id, + ) + #register this action + _all_actions_list.append(self) + for i in range(len(keypresses)/2): + keyval, mod_mask = keypresses[i*2:(i+1)*2] + #register this keypress + if _actions_keypress_dict.has_key((keyval, mod_mask)): + raise KeyError('keyval/mod_mask pair already registered "%s"'%str((keyval, mod_mask))) + _actions_keypress_dict[(keyval, mod_mask)] = self + #set the accelerator group, and accelerator path + #register the key name and mod mask with the accelerator path + if label is None: continue #dont register accel + accel_path = '<main>/'+self.get_name() + self.set_accel_group(get_accel_group()) + self.set_accel_path(accel_path) + gtk.accel_map_add_entry(accel_path, keyval, mod_mask) - def __str__(self): - """ - The string representation should be the name of the action id. - Try to find the action id for this action by searching this module. - """ - try: - import Actions - return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0] - except: return self.get_name() + def __str__(self): + """ + The string representation should be the name of the action id. + Try to find the action id for this action by searching this module. + """ + try: + import Actions + return filter(lambda attr: getattr(Actions, attr) == self, dir(Actions))[0] + except: return self.get_name() - def __repr__(self): return str(self) + def __repr__(self): return str(self) - def __call__(self): - """ - Emit the activate signal when called with (). - """ - self.emit('activate') + def __call__(self): + """ + Emit the activate signal when called with (). + """ + self.emit('activate') ######################################################################## # Actions ######################################################################## PAGE_CHANGE = Action() FLOW_GRAPH_NEW = Action( - label='_New', - tooltip='Create a new flow graph', - stock_id=gtk.STOCK_NEW, - keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK), + label='_New', + tooltip='Create a new flow graph', + stock_id=gtk.STOCK_NEW, + keypresses=(gtk.keysyms.n, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_OPEN = Action( - label='_Open', - tooltip='Open an existing flow graph', - stock_id=gtk.STOCK_OPEN, - keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK), + label='_Open', + tooltip='Open an existing flow graph', + stock_id=gtk.STOCK_OPEN, + keypresses=(gtk.keysyms.o, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_SAVE = Action( - label='_Save', - tooltip='Save the current flow graph', - stock_id=gtk.STOCK_SAVE, - keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK), + label='_Save', + tooltip='Save the current flow graph', + stock_id=gtk.STOCK_SAVE, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_SAVE_AS = Action( - label='Save _As', - tooltip='Save the current flow graph as...', - stock_id=gtk.STOCK_SAVE_AS, - keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), + label='Save _As', + tooltip='Save the current flow graph as...', + stock_id=gtk.STOCK_SAVE_AS, + keypresses=(gtk.keysyms.s, gtk.gdk.CONTROL_MASK | gtk.gdk.SHIFT_MASK), ) FLOW_GRAPH_CLOSE = Action( - label='_Close', - tooltip='Close the current flow graph', - stock_id=gtk.STOCK_CLOSE, - keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK), + label='_Close', + tooltip='Close the current flow graph', + stock_id=gtk.STOCK_CLOSE, + keypresses=(gtk.keysyms.w, gtk.gdk.CONTROL_MASK), ) APPLICATION_INITIALIZE = Action() APPLICATION_QUIT = Action( - label='_Quit', - tooltip='Quit program', - stock_id=gtk.STOCK_QUIT, - keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK), + label='_Quit', + tooltip='Quit program', + stock_id=gtk.STOCK_QUIT, + keypresses=(gtk.keysyms.q, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_UNDO = Action( - label='_Undo', - tooltip='Undo a change to the flow graph', - stock_id=gtk.STOCK_UNDO, - keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK), + label='_Undo', + tooltip='Undo a change to the flow graph', + stock_id=gtk.STOCK_UNDO, + keypresses=(gtk.keysyms.z, gtk.gdk.CONTROL_MASK), ) FLOW_GRAPH_REDO = Action( - label='_Redo', - tooltip='Redo a change to the flow graph', - stock_id=gtk.STOCK_REDO, - keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), + label='_Redo', + tooltip='Redo a change to the flow graph', + stock_id=gtk.STOCK_REDO, + keypresses=(gtk.keysyms.y, gtk.gdk.CONTROL_MASK), ) NOTHING_SELECT = Action() ELEMENT_SELECT = Action() ELEMENT_CREATE = Action() ELEMENT_DELETE = Action( - label='_Delete', - tooltip='Delete the selected blocks', - stock_id=gtk.STOCK_DELETE, - keypresses=(gtk.keysyms.Delete, NO_MODS_MASK), + label='_Delete', + tooltip='Delete the selected blocks', + stock_id=gtk.STOCK_DELETE, + keypresses=(gtk.keysyms.Delete, NO_MODS_MASK), ) BLOCK_MOVE = Action() BLOCK_ROTATE_CCW = Action( - label='Rotate Counterclockwise', - tooltip='Rotate the selected blocks 90 degrees to the left', - stock_id=gtk.STOCK_GO_BACK, - keypresses=(gtk.keysyms.Left, NO_MODS_MASK), + label='Rotate Counterclockwise', + tooltip='Rotate the selected blocks 90 degrees to the left', + stock_id=gtk.STOCK_GO_BACK, + keypresses=(gtk.keysyms.Left, NO_MODS_MASK), ) BLOCK_ROTATE_CW = Action( - label='Rotate Clockwise', - tooltip='Rotate the selected blocks 90 degrees to the right', - stock_id=gtk.STOCK_GO_FORWARD, - keypresses=(gtk.keysyms.Right, NO_MODS_MASK), + label='Rotate Clockwise', + tooltip='Rotate the selected blocks 90 degrees to the right', + stock_id=gtk.STOCK_GO_FORWARD, + keypresses=(gtk.keysyms.Right, NO_MODS_MASK), ) BLOCK_PARAM_MODIFY = Action( - label='_Properties', - tooltip='Modify params for the selected block', - stock_id=gtk.STOCK_PROPERTIES, - keypresses=(gtk.keysyms.Return, NO_MODS_MASK), + label='_Properties', + tooltip='Modify params for the selected block', + stock_id=gtk.STOCK_PROPERTIES, + keypresses=(gtk.keysyms.Return, NO_MODS_MASK), ) BLOCK_ENABLE = Action( - label='E_nable', - tooltip='Enable the selected blocks', - stock_id=gtk.STOCK_CONNECT, - keypresses=(gtk.keysyms.e, NO_MODS_MASK), + label='E_nable', + tooltip='Enable the selected blocks', + stock_id=gtk.STOCK_CONNECT, + keypresses=(gtk.keysyms.e, NO_MODS_MASK), ) BLOCK_DISABLE = Action( - label='D_isable', - tooltip='Disable the selected blocks', - stock_id=gtk.STOCK_DISCONNECT, - keypresses=(gtk.keysyms.d, NO_MODS_MASK), + label='D_isable', + tooltip='Disable the selected blocks', + stock_id=gtk.STOCK_DISCONNECT, + keypresses=(gtk.keysyms.d, NO_MODS_MASK), ) BLOCK_CREATE_HIER = Action( - label='C_reate Hier', - tooltip='Create hier block from selected blocks', - stock_id=gtk.STOCK_CONNECT, -# keypresses=(gtk.keysyms.c, NO_MODS_MASK), + label='C_reate Hier', + tooltip='Create hier block from selected blocks', + stock_id=gtk.STOCK_CONNECT, +# keypresses=(gtk.keysyms.c, NO_MODS_MASK), ) BLOCK_CUT = Action( - label='Cu_t', - tooltip='Cut', - stock_id=gtk.STOCK_CUT, - keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK), + label='Cu_t', + tooltip='Cut', + stock_id=gtk.STOCK_CUT, + keypresses=(gtk.keysyms.x, gtk.gdk.CONTROL_MASK), ) BLOCK_COPY = Action( - label='_Copy', - tooltip='Copy', - stock_id=gtk.STOCK_COPY, - keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK), + label='_Copy', + tooltip='Copy', + stock_id=gtk.STOCK_COPY, + keypresses=(gtk.keysyms.c, gtk.gdk.CONTROL_MASK), ) BLOCK_PASTE = Action( - label='_Paste', - tooltip='Paste', - stock_id=gtk.STOCK_PASTE, - keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK), + label='_Paste', + tooltip='Paste', + stock_id=gtk.STOCK_PASTE, + keypresses=(gtk.keysyms.v, gtk.gdk.CONTROL_MASK), ) ERRORS_WINDOW_DISPLAY = Action( - label='_Errors', - tooltip='View flow graph errors', - stock_id=gtk.STOCK_DIALOG_ERROR, + label='_Errors', + tooltip='View flow graph errors', + stock_id=gtk.STOCK_DIALOG_ERROR, ) ABOUT_WINDOW_DISPLAY = Action( - label='_About', - tooltip='About this program', - stock_id=gtk.STOCK_ABOUT, + label='_About', + tooltip='About this program', + stock_id=gtk.STOCK_ABOUT, ) HELP_WINDOW_DISPLAY = Action( - label='_Help', - tooltip='Usage tips', - stock_id=gtk.STOCK_HELP, - keypresses=(gtk.keysyms.F1, NO_MODS_MASK), + label='_Help', + tooltip='Usage tips', + stock_id=gtk.STOCK_HELP, + keypresses=(gtk.keysyms.F1, NO_MODS_MASK), ) TYPES_WINDOW_DISPLAY = Action( - label='_Types', - tooltip='Types color mapping', - stock_id=gtk.STOCK_DIALOG_INFO, + label='_Types', + tooltip='Types color mapping', + stock_id=gtk.STOCK_DIALOG_INFO, ) FLOW_GRAPH_GEN = Action( - label='_Generate', - tooltip='Generate the flow graph', - stock_id=gtk.STOCK_CONVERT, - keypresses=(gtk.keysyms.F5, NO_MODS_MASK), + label='_Generate', + tooltip='Generate the flow graph', + stock_id=gtk.STOCK_CONVERT, + keypresses=(gtk.keysyms.F5, NO_MODS_MASK), ) FLOW_GRAPH_EXEC = Action( - label='_Execute', - tooltip='Execute the flow graph', - stock_id=gtk.STOCK_EXECUTE, - keypresses=(gtk.keysyms.F6, NO_MODS_MASK), + label='_Execute', + tooltip='Execute the flow graph', + stock_id=gtk.STOCK_EXECUTE, + keypresses=(gtk.keysyms.F6, NO_MODS_MASK), ) FLOW_GRAPH_KILL = Action( - label='_Kill', - tooltip='Kill the flow graph', - stock_id=gtk.STOCK_STOP, - keypresses=(gtk.keysyms.F7, NO_MODS_MASK), + label='_Kill', + tooltip='Kill the flow graph', + stock_id=gtk.STOCK_STOP, + keypresses=(gtk.keysyms.F7, NO_MODS_MASK), ) FLOW_GRAPH_SCREEN_CAPTURE = Action( - label='S_creen Capture', - tooltip='Create a screen capture of the flow graph', - stock_id=gtk.STOCK_PRINT, - keypresses=(gtk.keysyms.Print, NO_MODS_MASK), + label='S_creen Capture', + tooltip='Create a screen capture of the flow graph', + stock_id=gtk.STOCK_PRINT, + keypresses=(gtk.keysyms.Print, NO_MODS_MASK), ) PORT_CONTROLLER_DEC = Action( - keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK), + keypresses=(gtk.keysyms.minus, NO_MODS_MASK, gtk.keysyms.KP_Subtract, NO_MODS_MASK), ) PORT_CONTROLLER_INC = Action( - keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK), + keypresses=(gtk.keysyms.plus, NO_MODS_MASK, gtk.keysyms.KP_Add, NO_MODS_MASK), ) BLOCK_INC_TYPE = Action( - keypresses=(gtk.keysyms.Down, NO_MODS_MASK), + keypresses=(gtk.keysyms.Down, NO_MODS_MASK), ) BLOCK_DEC_TYPE = Action( - keypresses=(gtk.keysyms.Up, NO_MODS_MASK), + keypresses=(gtk.keysyms.Up, NO_MODS_MASK), ) RELOAD_BLOCKS = Action( - label='Reload _Blocks', - tooltip='Reload Blocks', - stock_id=gtk.STOCK_REFRESH + label='Reload _Blocks', + tooltip='Reload Blocks', + stock_id=gtk.STOCK_REFRESH ) OPEN_HIER = Action( - label='Open H_ier', - tooltip='Open the source of the selected hierarchical block', - stock_id=gtk.STOCK_JUMP_TO, + label='Open H_ier', + tooltip='Open the source of the selected hierarchical block', + stock_id=gtk.STOCK_JUMP_TO, +) +BUSSIFY_SOURCES = Action( + label='Toggle So_urce Bus', + tooltip='Gang source ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, +) +BUSSIFY_SINKS = Action( + label='Toggle S_ink Bus', + tooltip='Gang sink ports into a single bus port', + stock_id=gtk.STOCK_JUMP_TO, ) diff --git a/grc/gui/Bars.py b/grc/gui/Bars.py index d95d23f1fe..e2b7f4f9bc 100644 --- a/grc/gui/Bars.py +++ b/grc/gui/Bars.py @@ -24,120 +24,121 @@ import gtk ##The list of actions for the toolbar. TOOLBAR_LIST = ( - Actions.FLOW_GRAPH_NEW, - Actions.FLOW_GRAPH_OPEN, - Actions.FLOW_GRAPH_SAVE, - Actions.FLOW_GRAPH_CLOSE, - None, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, - None, - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - None, - Actions.FLOW_GRAPH_UNDO, - Actions.FLOW_GRAPH_REDO, - None, - Actions.ERRORS_WINDOW_DISPLAY, - Actions.FLOW_GRAPH_GEN, - Actions.FLOW_GRAPH_EXEC, - Actions.FLOW_GRAPH_KILL, - None, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - None, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, + Actions.FLOW_GRAPH_NEW, + Actions.FLOW_GRAPH_OPEN, + Actions.FLOW_GRAPH_SAVE, + Actions.FLOW_GRAPH_CLOSE, + None, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, + None, + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + None, + Actions.FLOW_GRAPH_UNDO, + Actions.FLOW_GRAPH_REDO, + None, + Actions.ERRORS_WINDOW_DISPLAY, + Actions.FLOW_GRAPH_GEN, + Actions.FLOW_GRAPH_EXEC, + Actions.FLOW_GRAPH_KILL, + None, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + None, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, None, Actions.RELOAD_BLOCKS, Actions.OPEN_HIER, + Actions.BUSSIFY_SOURCES, ) ##The list of actions and categories for the menu bar. MENU_BAR_LIST = ( - (gtk.Action('File', '_File', None, None), [ - Actions.FLOW_GRAPH_NEW, - Actions.FLOW_GRAPH_OPEN, - None, - Actions.FLOW_GRAPH_SAVE, - Actions.FLOW_GRAPH_SAVE_AS, - None, - Actions.FLOW_GRAPH_SCREEN_CAPTURE, - None, - Actions.FLOW_GRAPH_CLOSE, - Actions.APPLICATION_QUIT, - ]), - (gtk.Action('Edit', '_Edit', None, None), [ - Actions.FLOW_GRAPH_UNDO, - Actions.FLOW_GRAPH_REDO, - None, - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - None, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - None, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, - None, - Actions.BLOCK_PARAM_MODIFY, - ]), - (gtk.Action('View', '_View', None, None), [ - Actions.ERRORS_WINDOW_DISPLAY, - ]), - (gtk.Action('Build', '_Build', None, None), [ - Actions.FLOW_GRAPH_GEN, - Actions.FLOW_GRAPH_EXEC, - Actions.FLOW_GRAPH_KILL, - ]), - (gtk.Action('Help', '_Help', None, None), [ - Actions.HELP_WINDOW_DISPLAY, - Actions.TYPES_WINDOW_DISPLAY, - None, - Actions.ABOUT_WINDOW_DISPLAY, - ]), + (gtk.Action('File', '_File', None, None), [ + Actions.FLOW_GRAPH_NEW, + Actions.FLOW_GRAPH_OPEN, + None, + Actions.FLOW_GRAPH_SAVE, + Actions.FLOW_GRAPH_SAVE_AS, + None, + Actions.FLOW_GRAPH_SCREEN_CAPTURE, + None, + Actions.FLOW_GRAPH_CLOSE, + Actions.APPLICATION_QUIT, + ]), + (gtk.Action('Edit', '_Edit', None, None), [ + Actions.FLOW_GRAPH_UNDO, + Actions.FLOW_GRAPH_REDO, + None, + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + None, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + None, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, + None, + Actions.BLOCK_PARAM_MODIFY, + ]), + (gtk.Action('View', '_View', None, None), [ + Actions.ERRORS_WINDOW_DISPLAY, + ]), + (gtk.Action('Build', '_Build', None, None), [ + Actions.FLOW_GRAPH_GEN, + Actions.FLOW_GRAPH_EXEC, + Actions.FLOW_GRAPH_KILL, + ]), + (gtk.Action('Help', '_Help', None, None), [ + Actions.HELP_WINDOW_DISPLAY, + Actions.TYPES_WINDOW_DISPLAY, + None, + Actions.ABOUT_WINDOW_DISPLAY, + ]), ) class Toolbar(gtk.Toolbar): - """The gtk toolbar with actions added from the toolbar list.""" + """The gtk toolbar with actions added from the toolbar list.""" - def __init__(self): - """ - Parse the list of action names in the toolbar list. - Look up the action for each name in the action list and add it to the toolbar. - """ - gtk.Toolbar.__init__(self) - self.set_style(gtk.TOOLBAR_ICONS) - for action in TOOLBAR_LIST: - if action: #add a tool item - self.add(action.create_tool_item()) - #this reset of the tooltip property is required (after creating the tool item) for the tooltip to show - action.set_property('tooltip', action.get_property('tooltip')) - else: self.add(gtk.SeparatorToolItem()) + def __init__(self): + """ + Parse the list of action names in the toolbar list. + Look up the action for each name in the action list and add it to the toolbar. + """ + gtk.Toolbar.__init__(self) + self.set_style(gtk.TOOLBAR_ICONS) + for action in TOOLBAR_LIST: + if action: #add a tool item + self.add(action.create_tool_item()) + #this reset of the tooltip property is required (after creating the tool item) for the tooltip to show + action.set_property('tooltip', action.get_property('tooltip')) + else: self.add(gtk.SeparatorToolItem()) class MenuBar(gtk.MenuBar): - """The gtk menu bar with actions added from the menu bar list.""" + """The gtk menu bar with actions added from the menu bar list.""" - def __init__(self): - """ - Parse the list of submenus from the menubar list. - For each submenu, get a list of action names. - Look up the action for each name in the action list and add it to the submenu. - Add the submenu to the menu bar. - """ - gtk.MenuBar.__init__(self) - for main_action, actions in MENU_BAR_LIST: - #create the main menu item - main_menu_item = main_action.create_menu_item() - self.append(main_menu_item) - #create the menu - main_menu = gtk.Menu() - main_menu_item.set_submenu(main_menu) - for action in actions: - if action: #append a menu item - main_menu.append(action.create_menu_item()) - else: main_menu.append(gtk.SeparatorMenuItem()) - main_menu.show_all() #this show all is required for the separators to show + def __init__(self): + """ + Parse the list of submenus from the menubar list. + For each submenu, get a list of action names. + Look up the action for each name in the action list and add it to the submenu. + Add the submenu to the menu bar. + """ + gtk.MenuBar.__init__(self) + for main_action, actions in MENU_BAR_LIST: + #create the main menu item + main_menu_item = main_action.create_menu_item() + self.append(main_menu_item) + #create the menu + main_menu = gtk.Menu() + main_menu_item.set_submenu(main_menu) + for action in actions: + if action: #append a menu item + main_menu.append(action.create_menu_item()) + else: main_menu.append(gtk.SeparatorMenuItem()) + main_menu.show_all() #this show all is required for the separators to show diff --git a/grc/gui/Block.py b/grc/gui/Block.py index 11e66bff85..30031866c0 100644 --- a/grc/gui/Block.py +++ b/grc/gui/Block.py @@ -23,9 +23,9 @@ import Colors from .. base import odict from Constants import BORDER_PROXIMITY_SENSITIVITY from Constants import \ - BLOCK_LABEL_PADDING, \ - PORT_SEPARATION, LABEL_SEPARATION, \ - PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS + BLOCK_LABEL_PADDING, \ + PORT_SEPARATION, LABEL_SEPARATION, \ + PORT_BORDER_SEPARATION, POSSIBLE_ROTATIONS import pygtk pygtk.require('2.0') import gtk @@ -36,180 +36,184 @@ BLOCK_MARKUP_TMPL="""\ <span foreground="$foreground" font_desc="Sans 8"><b>$encode($block.get_name())</b></span>""" class Block(Element): - """The graphical signal block.""" + """The graphical signal block.""" - def __init__(self): - """ - Block contructor. - Add graphics related params to the block. - """ - #add the position param - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Coordinate', - 'key': '_coordinate', - 'type': 'raw', - 'value': '(0, 0)', - 'hide': 'all', - }) - )) - self.get_params().append(self.get_parent().get_parent().Param( - block=self, - n=odict({ - 'name': 'GUI Rotation', - 'key': '_rotation', - 'type': 'raw', - 'value': '0', - 'hide': 'all', - }) - )) - Element.__init__(self) + def __init__(self): + """ + Block contructor. + Add graphics related params to the block. + """ + #add the position param + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'GUI Coordinate', + 'key': '_coordinate', + 'type': 'raw', + 'value': '(0, 0)', + 'hide': 'all', + }) + )) + self.get_params().append(self.get_parent().get_parent().Param( + block=self, + n=odict({ + 'name': 'GUI Rotation', + 'key': '_rotation', + 'type': 'raw', + 'value': '0', + 'hide': 'all', + }) + )) + Element.__init__(self) - def get_coordinate(self): - """ - Get the coordinate from the position param. - - Returns: - the coordinate tuple (x, y) or (0, 0) if failure - """ - try: #should evaluate to tuple - coor = eval(self.get_param('_coordinate').get_value()) - x, y = map(int, coor) - fgW,fgH = self.get_parent().get_size() - if x <= 0: - x = 0 - elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: - x = fgW - BORDER_PROXIMITY_SENSITIVITY - if y <= 0: - y = 0 - elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: - y = fgH - BORDER_PROXIMITY_SENSITIVITY - return (x, y) - except: - self.set_coordinate((0, 0)) - return (0, 0) + def get_coordinate(self): + """ + Get the coordinate from the position param. + + Returns: + the coordinate tuple (x, y) or (0, 0) if failure + """ + try: #should evaluate to tuple + coor = eval(self.get_param('_coordinate').get_value()) + x, y = map(int, coor) + fgW,fgH = self.get_parent().get_size() + if x <= 0: + x = 0 + elif x >= fgW - BORDER_PROXIMITY_SENSITIVITY: + x = fgW - BORDER_PROXIMITY_SENSITIVITY + if y <= 0: + y = 0 + elif y >= fgH - BORDER_PROXIMITY_SENSITIVITY: + y = fgH - BORDER_PROXIMITY_SENSITIVITY + return (x, y) + except: + self.set_coordinate((0, 0)) + return (0, 0) - def set_coordinate(self, coor): - """ - Set the coordinate into the position param. - - Args: - coor: the coordinate tuple (x, y) - """ - self.get_param('_coordinate').set_value(str(coor)) + def set_coordinate(self, coor): + """ + Set the coordinate into the position param. + + Args: + coor: the coordinate tuple (x, y) + """ + self.get_param('_coordinate').set_value(str(coor)) - def get_rotation(self): - """ - Get the rotation from the position param. - - Returns: - the rotation in degrees or 0 if failure - """ - try: #should evaluate to dict - rotation = eval(self.get_param('_rotation').get_value()) - return int(rotation) - except: - self.set_rotation(POSSIBLE_ROTATIONS[0]) - return POSSIBLE_ROTATIONS[0] + def get_rotation(self): + """ + Get the rotation from the position param. + + Returns: + the rotation in degrees or 0 if failure + """ + try: #should evaluate to dict + rotation = eval(self.get_param('_rotation').get_value()) + return int(rotation) + except: + self.set_rotation(POSSIBLE_ROTATIONS[0]) + return POSSIBLE_ROTATIONS[0] - def set_rotation(self, rot): - """ - Set the rotation into the position param. - - Args: - rot: the rotation in degrees - """ - self.get_param('_rotation').set_value(str(rot)) + def set_rotation(self, rot): + """ + Set the rotation into the position param. + + Args: + rot: the rotation in degrees + """ + self.get_param('_rotation').set_value(str(rot)) - def create_shapes(self): - """Update the block, parameters, and ports when a change occurs.""" - Element.create_shapes(self) - if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H)) - elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W)) + def create_shapes(self): + """Update the block, parameters, and ports when a change occurs.""" + Element.create_shapes(self) + if self.is_horizontal(): self.add_area((0, 0), (self.W, self.H)) + elif self.is_vertical(): self.add_area((0, 0), (self.H, self.W)) - def create_labels(self): - """Create the labels for the signal block.""" - Element.create_labels(self) - self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR - layouts = list() - #create the main layout - layout = gtk.DrawingArea().create_pango_layout('') - layouts.append(layout) - layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self)) - self.label_width, self.label_height = layout.get_pixel_size() - #display the params - markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] - if markups: - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_spacing(LABEL_SEPARATION*pango.SCALE) - layout.set_markup('\n'.join(markups)) - layouts.append(layout) - w,h = layout.get_pixel_size() - self.label_width = max(w, self.label_width) - self.label_height += h + LABEL_SEPARATION - width = self.label_width - height = self.label_height - #setup the pixmap - pixmap = self.get_parent().new_pixmap(width, height) - gc = pixmap.new_gc() - gc.set_foreground(self._bg_color) - pixmap.draw_rectangle(gc, True, 0, 0, width, height) - #draw the layouts - h_off = 0 - for i,layout in enumerate(layouts): - w,h = layout.get_pixel_size() - if i == 0: w_off = (width-w)/2 - else: w_off = 0 - pixmap.draw_layout(gc, w_off, h_off, layout) - h_off = h + h_off + LABEL_SEPARATION - #create vertical and horizontal pixmaps - self.horizontal_label = pixmap - if self.is_vertical(): - self.vertical_label = self.get_parent().new_pixmap(height, width) - Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) - #calculate width and height needed - self.W = self.label_width + 2*BLOCK_LABEL_PADDING - self.H = max(*( - [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \ - sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION - for ports in (self.get_sources(), self.get_sinks())] - )) + def create_labels(self): + """Create the labels for the signal block.""" + Element.create_labels(self) + self._bg_color = self.get_enabled() and Colors.BLOCK_ENABLED_COLOR or Colors.BLOCK_DISABLED_COLOR + layouts = list() + #create the main layout + layout = gtk.DrawingArea().create_pango_layout('') + layouts.append(layout) + layout.set_markup(Utils.parse_template(BLOCK_MARKUP_TMPL, block=self)) + self.label_width, self.label_height = layout.get_pixel_size() + #display the params + markups = [param.get_markup() for param in self.get_params() if param.get_hide() not in ('all', 'part')] + if markups: + layout = gtk.DrawingArea().create_pango_layout('') + layout.set_spacing(LABEL_SEPARATION*pango.SCALE) + layout.set_markup('\n'.join(markups)) + layouts.append(layout) + w,h = layout.get_pixel_size() + self.label_width = max(w, self.label_width) + self.label_height += h + LABEL_SEPARATION + width = self.label_width + height = self.label_height + #setup the pixmap + pixmap = self.get_parent().new_pixmap(width, height) + gc = pixmap.new_gc() + gc.set_foreground(self._bg_color) + pixmap.draw_rectangle(gc, True, 0, 0, width, height) + #draw the layouts + h_off = 0 + for i,layout in enumerate(layouts): + w,h = layout.get_pixel_size() + if i == 0: w_off = (width-w)/2 + else: w_off = 0 + pixmap.draw_layout(gc, w_off, h_off, layout) + h_off = h + h_off + LABEL_SEPARATION + #create vertical and horizontal pixmaps + self.horizontal_label = pixmap + if self.is_vertical(): + self.vertical_label = self.get_parent().new_pixmap(height, width) + Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) + #calculate width and height needed + self.W = self.label_width + 2*BLOCK_LABEL_PADDING + self.H = max(*( + [self.label_height+2*BLOCK_LABEL_PADDING] + [2*PORT_BORDER_SEPARATION + \ + sum([port.H + PORT_SEPARATION for port in ports]) - PORT_SEPARATION + for ports in (self.get_sources_gui(), self.get_sinks_gui())] + + [4*PORT_BORDER_SEPARATION + \ + sum([(port.H) + PORT_SEPARATION for port in ports]) - PORT_SEPARATION + for ports in ([i for i in self.get_sources_gui() if i.get_type() == 'bus'], [i for i in self.get_sinks_gui() if i.get_type() == 'bus'])] + )) - def draw(self, gc, window): - """ - Draw the signal block with label and inputs/outputs. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - x, y = self.get_coordinate() - #draw main block - Element.draw( - self, gc, window, bg_color=self._bg_color, - border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, - ) - #draw label image - if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) - elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) - #draw ports - for port in self.get_ports(): port.draw(gc, window) + def draw(self, gc, window): + """ + Draw the signal block with label and inputs/outputs. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + x, y = self.get_coordinate() + #draw main block + Element.draw( + self, gc, window, bg_color=self._bg_color, + border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, + ) + #draw label image + if self.is_horizontal(): + window.draw_drawable(gc, self.horizontal_label, 0, 0, x+BLOCK_LABEL_PADDING, y+(self.H-self.label_height)/2, -1, -1) + elif self.is_vertical(): + window.draw_drawable(gc, self.vertical_label, 0, 0, x+(self.H-self.label_height)/2, y+BLOCK_LABEL_PADDING, -1, -1) + #draw ports + for port in self.get_ports_gui(): + port.draw(gc, window) - def what_is_selected(self, coor, coor_m=None): - """ - Get the element that is selected. - - Args: - coor: the (x,y) tuple - coor_m: the (x_m, y_m) tuple - - Returns: - this block, a port, or None - """ - for port in self.get_ports(): - port_selected = port.what_is_selected(coor, coor_m) - if port_selected: return port_selected - return Element.what_is_selected(self, coor, coor_m) + def what_is_selected(self, coor, coor_m=None): + """ + Get the element that is selected. + + Args: + coor: the (x,y) tuple + coor_m: the (x_m, y_m) tuple + + Returns: + this block, a port, or None + """ + for port in self.get_ports_gui(): + port_selected = port.what_is_selected(coor, coor_m) + if port_selected: return port_selected + return Element.what_is_selected(self, coor, coor_m) diff --git a/grc/gui/BlockTreeWindow.py b/grc/gui/BlockTreeWindow.py index 28867dce7c..ced6429c62 100644 --- a/grc/gui/BlockTreeWindow.py +++ b/grc/gui/BlockTreeWindow.py @@ -38,172 +38,173 @@ undocumented#slurp CAT_MARKUP_TMPL="""Category: $cat""" class BlockTreeWindow(gtk.VBox): - """The block selection panel.""" - - def __init__(self, platform, get_flow_graph): - """ - BlockTreeWindow constructor. - Create a tree view of the possible blocks in the platform. - The tree view nodes will be category names, the leaves will be block names. - A mouse double click or button press action will trigger the add block event. - - Args: - platform: the particular platform will all block prototypes - get_flow_graph: get the selected flow graph - """ - gtk.VBox.__init__(self) - self.platform = platform - self.get_flow_graph = get_flow_graph - #make the tree model for holding blocks - self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) - self.treeview = gtk.TreeView(self.treestore) - self.treeview.set_enable_search(False) #disable pop up search box - self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK) - self.treeview.connect('button-press-event', self._handle_mouse_button_press) - selection = self.treeview.get_selection() - selection.set_mode('single') - selection.connect('changed', self._handle_selection_change) - renderer = gtk.CellRendererText() - column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) - self.treeview.append_column(column) - #setup the search - self.treeview.set_enable_search(True) - self.treeview.set_search_equal_func(self._handle_search) - #try to enable the tooltips (available in pygtk 2.12 and above) - try: self.treeview.set_tooltip_column(DOC_INDEX) - except: pass - #setup sort order - column.set_sort_column_id(0) - #setup drag and drop - self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) - self.treeview.connect('drag-data-get', self._handle_drag_get_data) - #make the scrolled window to hold the tree view - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled_window.add_with_viewport(self.treeview) - scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) - self.pack_start(scrolled_window) - #add button - self.add_button = gtk.Button(None, gtk.STOCK_ADD) - self.add_button.connect('clicked', self._handle_add_button) - self.pack_start(self.add_button, False) - #map categories to iters, automatic mapping for root - self._categories = {tuple(): None} - #add blocks and categories - self.platform.load_block_tree(self) - #initialize - self._update_add_button() - - def clear(self): - self.treestore.clear(); - self._categories = {tuple(): None} - - - ############################################################ - ## Block Tree Methods - ############################################################ - def add_block(self, category, block=None): - """ - Add a block with category to this selection window. - Add only the category when block is None. - - Args: - category: the category list or path string - block: the block object or None - """ - if isinstance(category, str): category = category.split('/') - category = tuple(filter(lambda x: x, category)) #tuple is hashable - #add category and all sub categories - for i, cat_name in enumerate(category): - sub_category = category[:i+1] - if sub_category not in self._categories: - iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None) - self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) - self.treestore.set_value(iter, KEY_INDEX, '') - self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name)) - self._categories[sub_category] = iter - #add block - if block is None: return - iter = self.treestore.insert_before(self._categories[category], None) - self.treestore.set_value(iter, NAME_INDEX, block.get_name()) - self.treestore.set_value(iter, KEY_INDEX, block.get_key()) - self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) - - ############################################################ - ## Helper Methods - ############################################################ - def _get_selected_block_key(self): - """ - Get the currently selected block key. - - Returns: - the key of the selected block or a empty string - """ - selection = self.treeview.get_selection() - treestore, iter = selection.get_selected() - return iter and treestore.get_value(iter, KEY_INDEX) or '' - - def _update_add_button(self): - """ - Update the add button's sensitivity. - The button should be active only if a block is selected. - """ - key = self._get_selected_block_key() - self.add_button.set_sensitive(bool(key)) - - def _add_selected_block(self): - """ - Add the selected block with the given key to the flow graph. - """ - key = self._get_selected_block_key() - if key: self.get_flow_graph().add_new_block(key) - - ############################################################ - ## Event Handlers - ############################################################ - def _handle_search(self, model, column, key, iter): - #determine which blocks match the search key - blocks = self.get_flow_graph().get_parent().get_blocks() - matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks) - #remove the old search category - try: self.treestore.remove(self._categories.pop((self._search_category, ))) - except (KeyError, AttributeError): pass #nothing to remove - #create a search category - if not matching_blocks: return - self._search_category = 'Search: %s'%key - for block in matching_blocks: self.add_block(self._search_category, block) - #expand the search category - path = self.treestore.get_path(self._categories[(self._search_category, )]) - self.treeview.collapse_all() - self.treeview.expand_row(path, open_all=False) - - def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time): - """ - Handle a drag and drop by setting the key to the selection object. - This will call the destination handler for drag and drop. - Only call set when the key is valid to ignore DND from categories. - """ - key = self._get_selected_block_key() - if key: selection_data.set(selection_data.target, 8, key) - - def _handle_mouse_button_press(self, widget, event): - """ - Handle the mouse button press. - If a left double click is detected, call add selected block. - """ - if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: - self._add_selected_block() - - def _handle_selection_change(self, selection): - """ - Handle a selection change in the tree view. - If a selection changes, set the add button sensitive. - """ - self._update_add_button() - - def _handle_add_button(self, widget): - """ - Handle the add button clicked signal. - Call add selected block. - """ - self._add_selected_block() + """The block selection panel.""" + + def __init__(self, platform, get_flow_graph): + """ + BlockTreeWindow constructor. + Create a tree view of the possible blocks in the platform. + The tree view nodes will be category names, the leaves will be block names. + A mouse double click or button press action will trigger the add block event. + + Args: + platform: the particular platform will all block prototypes + get_flow_graph: get the selected flow graph + """ + gtk.VBox.__init__(self) + self.platform = platform + self.get_flow_graph = get_flow_graph + #make the tree model for holding blocks + self.treestore = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_STRING, gobject.TYPE_STRING) + self.treeview = gtk.TreeView(self.treestore) + self.treeview.set_enable_search(False) #disable pop up search box + self.treeview.add_events(gtk.gdk.BUTTON_PRESS_MASK) + self.treeview.connect('button-press-event', self._handle_mouse_button_press) + selection = self.treeview.get_selection() + selection.set_mode('single') + selection.connect('changed', self._handle_selection_change) + renderer = gtk.CellRendererText() + column = gtk.TreeViewColumn('Blocks', renderer, text=NAME_INDEX) + self.treeview.append_column(column) + #setup the search + self.treeview.set_enable_search(True) + self.treeview.set_search_equal_func(self._handle_search) + #try to enable the tooltips (available in pygtk 2.12 and above) + try: self.treeview.set_tooltip_column(DOC_INDEX) + except: pass + #setup sort order + column.set_sort_column_id(0) + self.treestore.set_sort_column_id(0, gtk.SORT_ASCENDING) + #setup drag and drop + self.treeview.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.treeview.connect('drag-data-get', self._handle_drag_get_data) + #make the scrolled window to hold the tree view + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_window.add_with_viewport(self.treeview) + scrolled_window.set_size_request(DEFAULT_BLOCKS_WINDOW_WIDTH, -1) + self.pack_start(scrolled_window) + #add button + self.add_button = gtk.Button(None, gtk.STOCK_ADD) + self.add_button.connect('clicked', self._handle_add_button) + self.pack_start(self.add_button, False) + #map categories to iters, automatic mapping for root + self._categories = {tuple(): None} + #add blocks and categories + self.platform.load_block_tree(self) + #initialize + self._update_add_button() + + def clear(self): + self.treestore.clear(); + self._categories = {tuple(): None} + + + ############################################################ + ## Block Tree Methods + ############################################################ + def add_block(self, category, block=None): + """ + Add a block with category to this selection window. + Add only the category when block is None. + + Args: + category: the category list or path string + block: the block object or None + """ + if isinstance(category, str): category = category.split('/') + category = tuple(filter(lambda x: x, category)) #tuple is hashable + #add category and all sub categories + for i, cat_name in enumerate(category): + sub_category = category[:i+1] + if sub_category not in self._categories: + iter = self.treestore.insert_before(self._categories[sub_category[:-1]], None) + self.treestore.set_value(iter, NAME_INDEX, '[ %s ]'%cat_name) + self.treestore.set_value(iter, KEY_INDEX, '') + self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(CAT_MARKUP_TMPL, cat=cat_name)) + self._categories[sub_category] = iter + #add block + if block is None: return + iter = self.treestore.insert_before(self._categories[category], None) + self.treestore.set_value(iter, NAME_INDEX, block.get_name()) + self.treestore.set_value(iter, KEY_INDEX, block.get_key()) + self.treestore.set_value(iter, DOC_INDEX, Utils.parse_template(DOC_MARKUP_TMPL, doc=block.get_doc())) + + ############################################################ + ## Helper Methods + ############################################################ + def _get_selected_block_key(self): + """ + Get the currently selected block key. + + Returns: + the key of the selected block or a empty string + """ + selection = self.treeview.get_selection() + treestore, iter = selection.get_selected() + return iter and treestore.get_value(iter, KEY_INDEX) or '' + + def _update_add_button(self): + """ + Update the add button's sensitivity. + The button should be active only if a block is selected. + """ + key = self._get_selected_block_key() + self.add_button.set_sensitive(bool(key)) + + def _add_selected_block(self): + """ + Add the selected block with the given key to the flow graph. + """ + key = self._get_selected_block_key() + if key: self.get_flow_graph().add_new_block(key) + + ############################################################ + ## Event Handlers + ############################################################ + def _handle_search(self, model, column, key, iter): + #determine which blocks match the search key + blocks = self.get_flow_graph().get_parent().get_blocks() + matching_blocks = filter(lambda b: key in b.get_key() or key in b.get_name().lower(), blocks) + #remove the old search category + try: self.treestore.remove(self._categories.pop((self._search_category, ))) + except (KeyError, AttributeError): pass #nothing to remove + #create a search category + if not matching_blocks: return + self._search_category = 'Search: %s'%key + for block in matching_blocks: self.add_block(self._search_category, block) + #expand the search category + path = self.treestore.get_path(self._categories[(self._search_category, )]) + self.treeview.collapse_all() + self.treeview.expand_row(path, open_all=False) + + def _handle_drag_get_data(self, widget, drag_context, selection_data, info, time): + """ + Handle a drag and drop by setting the key to the selection object. + This will call the destination handler for drag and drop. + Only call set when the key is valid to ignore DND from categories. + """ + key = self._get_selected_block_key() + if key: selection_data.set(selection_data.target, 8, key) + + def _handle_mouse_button_press(self, widget, event): + """ + Handle the mouse button press. + If a left double click is detected, call add selected block. + """ + if event.button == 1 and event.type == gtk.gdk._2BUTTON_PRESS: + self._add_selected_block() + + def _handle_selection_change(self, selection): + """ + Handle a selection change in the tree view. + If a selection changes, set the add button sensitive. + """ + self._update_add_button() + + def _handle_add_button(self, widget): + """ + Handle the add button clicked signal. + Call add selected block. + """ + self._add_selected_block() diff --git a/grc/gui/Connection.py b/grc/gui/Connection.py index 5d24fefded..0f631791db 100644 --- a/grc/gui/Connection.py +++ b/grc/gui/Connection.py @@ -23,127 +23,137 @@ import Colors from Constants import CONNECTOR_ARROW_BASE, CONNECTOR_ARROW_HEIGHT class Connection(Element): - """ - A graphical connection for ports. - The connection has 2 parts, the arrow and the wire. - The coloring of the arrow and wire exposes the status of 3 states: - enabled/disabled, valid/invalid, highlighted/non-highlighted. - The wire coloring exposes the enabled and highlighted states. - The arrow coloring exposes the enabled and valid states. - """ + """ + A graphical connection for ports. + The connection has 2 parts, the arrow and the wire. + The coloring of the arrow and wire exposes the status of 3 states: + enabled/disabled, valid/invalid, highlighted/non-highlighted. + The wire coloring exposes the enabled and highlighted states. + The arrow coloring exposes the enabled and valid states. + """ - def __init__(self): Element.__init__(self) + def __init__(self): Element.__init__(self) - def get_coordinate(self): - """ - Get the 0,0 coordinate. - Coordinates are irrelevant in connection. - - Returns: - 0, 0 - """ - return (0, 0) + def get_coordinate(self): + """ + Get the 0,0 coordinate. + Coordinates are irrelevant in connection. + + Returns: + 0, 0 + """ + return (0, 0) - def get_rotation(self): - """ - Get the 0 degree rotation. - Rotations are irrelevant in connection. - - Returns: - 0 - """ - return 0 + def get_rotation(self): + """ + Get the 0 degree rotation. + Rotations are irrelevant in connection. + + Returns: + 0 + """ + return 0 - def create_shapes(self): - """Precalculate relative coordinates.""" - Element.create_shapes(self) - self._sink_rot = None - self._source_rot = None - self._sink_coor = None - self._source_coor = None - #get the source coordinate - connector_length = self.get_source().get_connector_length() - self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.get_source().get_rotation()) - #get the sink coordinate - connector_length = self.get_sink().get_connector_length() + CONNECTOR_ARROW_HEIGHT - self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.get_sink().get_rotation()) - #build the arrow - self.arrow = [(0, 0), - Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), - Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), - ] - self._update_after_move() - if not self.get_enabled(): self._arrow_color = Colors.CONNECTION_DISABLED_COLOR - elif not self.is_valid(): self._arrow_color = Colors.CONNECTION_ERROR_COLOR - else: self._arrow_color = Colors.CONNECTION_ENABLED_COLOR + def create_shapes(self): + """Precalculate relative coordinates.""" + Element.create_shapes(self) + self._sink_rot = None + self._source_rot = None + self._sink_coor = None + self._source_coor = None + #get the source coordinate + try: + connector_length = self.get_source().get_connector_length() + except: + return + self.x1, self.y1 = Utils.get_rotated_coordinate((connector_length, 0), self.get_source().get_rotation()) + #get the sink coordinate + connector_length = self.get_sink().get_connector_length() + CONNECTOR_ARROW_HEIGHT + self.x2, self.y2 = Utils.get_rotated_coordinate((-connector_length, 0), self.get_sink().get_rotation()) + #build the arrow + self.arrow = [(0, 0), + Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, -CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), + Utils.get_rotated_coordinate((-CONNECTOR_ARROW_HEIGHT, CONNECTOR_ARROW_BASE/2), self.get_sink().get_rotation()), + ] + self._update_after_move() + if not self.get_enabled(): self._arrow_color = Colors.CONNECTION_DISABLED_COLOR + elif not self.is_valid(): self._arrow_color = Colors.CONNECTION_ERROR_COLOR + else: self._arrow_color = Colors.CONNECTION_ENABLED_COLOR - def _update_after_move(self): - """Calculate coordinates.""" - self.clear() #FIXME do i want this here? - #source connector - source = self.get_source() - X, Y = source.get_connector_coordinate() - x1, y1 = self.x1 + X, self.y1 + Y - self.add_line((x1, y1), (X, Y)) - #sink connector - sink = self.get_sink() - X, Y = sink.get_connector_coordinate() - x2, y2 = self.x2 + X, self.y2 + Y - self.add_line((x2, y2), (X, Y)) - #adjust arrow - self._arrow = [(x+X, y+Y) for x,y in self.arrow] - #add the horizontal and vertical lines in this connection - if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180: - #2 possible point sets to create a 3-line connector - mid_x, mid_y = (x1 + x2)/2.0, (y1 + y2)/2.0 - points = [((mid_x, y1), (mid_x, y2)), ((x1, mid_y), (x2, mid_y))] - #source connector -> points[0][0] should be in the direction of source (if possible) - if Utils.get_angle_from_coordinates((x1, y1), points[0][0]) != source.get_connector_direction(): points.reverse() - #points[0][0] -> sink connector should not be in the direction of sink - if Utils.get_angle_from_coordinates(points[0][0], (x2, y2)) == sink.get_connector_direction(): points.reverse() - #points[0][0] -> source connector should not be in the direction of source - if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse() - #create 3-line connector - p1, p2 = map(int, points[0][0]), map(int, points[0][1]) - self.add_line((x1, y1), p1) - self.add_line(p1, p2) - self.add_line((x2, y2), p2) - else: - #2 possible points to create a right-angled connector - points = [(x1, y2), (x2, y1)] - #source connector -> points[0] should be in the direction of source (if possible) - if Utils.get_angle_from_coordinates((x1, y1), points[0]) != source.get_connector_direction(): points.reverse() - #points[0] -> sink connector should not be in the direction of sink - if Utils.get_angle_from_coordinates(points[0], (x2, y2)) == sink.get_connector_direction(): points.reverse() - #points[0] -> source connector should not be in the direction of source - if Utils.get_angle_from_coordinates(points[0], (x1, y1)) == source.get_connector_direction(): points.reverse() - #create right-angled connector - self.add_line((x1, y1), points[0]) - self.add_line((x2, y2), points[0]) + def _update_after_move(self): + """Calculate coordinates.""" + self.clear() #FIXME do i want this here? + #source connector + source = self.get_source() + X, Y = source.get_connector_coordinate() + x1, y1 = self.x1 + X, self.y1 + Y + self.add_line((x1, y1), (X, Y)) + #sink connector + sink = self.get_sink() + X, Y = sink.get_connector_coordinate() + x2, y2 = self.x2 + X, self.y2 + Y + self.add_line((x2, y2), (X, Y)) + #adjust arrow + self._arrow = [(x+X, y+Y) for x,y in self.arrow] + #add the horizontal and vertical lines in this connection + if abs(source.get_connector_direction() - sink.get_connector_direction()) == 180: + #2 possible point sets to create a 3-line connector + mid_x, mid_y = (x1 + x2)/2.0, (y1 + y2)/2.0 + points = [((mid_x, y1), (mid_x, y2)), ((x1, mid_y), (x2, mid_y))] + #source connector -> points[0][0] should be in the direction of source (if possible) + if Utils.get_angle_from_coordinates((x1, y1), points[0][0]) != source.get_connector_direction(): points.reverse() + #points[0][0] -> sink connector should not be in the direction of sink + if Utils.get_angle_from_coordinates(points[0][0], (x2, y2)) == sink.get_connector_direction(): points.reverse() + #points[0][0] -> source connector should not be in the direction of source + if Utils.get_angle_from_coordinates(points[0][0], (x1, y1)) == source.get_connector_direction(): points.reverse() + #create 3-line connector + p1, p2 = map(int, points[0][0]), map(int, points[0][1]) + self.add_line((x1, y1), p1) + self.add_line(p1, p2) + self.add_line((x2, y2), p2) + else: + #2 possible points to create a right-angled connector + points = [(x1, y2), (x2, y1)] + #source connector -> points[0] should be in the direction of source (if possible) + if Utils.get_angle_from_coordinates((x1, y1), points[0]) != source.get_connector_direction(): points.reverse() + #points[0] -> sink connector should not be in the direction of sink + if Utils.get_angle_from_coordinates(points[0], (x2, y2)) == sink.get_connector_direction(): points.reverse() + #points[0] -> source connector should not be in the direction of source + if Utils.get_angle_from_coordinates(points[0], (x1, y1)) == source.get_connector_direction(): points.reverse() + #create right-angled connector + self.add_line((x1, y1), points[0]) + self.add_line((x2, y2), points[0]) - def draw(self, gc, window): - """ - Draw the connection. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - sink = self.get_sink() - source = self.get_source() - #check for changes - if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes() - elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): self._update_after_move() - #cache values - self._sink_rot = sink.get_rotation() - self._source_rot = source.get_rotation() - self._sink_coor = sink.get_coordinate() - self._source_coor = source.get_coordinate() - #draw - if self.is_highlighted(): border_color = Colors.HIGHLIGHT_COLOR - elif self.get_enabled(): border_color = Colors.CONNECTION_ENABLED_COLOR - else: border_color = Colors.CONNECTION_DISABLED_COLOR - Element.draw(self, gc, window, bg_color=None, border_color=border_color) - #draw arrow on sink port - gc.set_foreground(self._arrow_color) - window.draw_polygon(gc, True, self._arrow) + def draw(self, gc, window): + """ + Draw the connection. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + sink = self.get_sink() + source = self.get_source() + #check for changes + if self._sink_rot != sink.get_rotation() or self._source_rot != source.get_rotation(): self.create_shapes() + elif self._sink_coor != sink.get_coordinate() or self._source_coor != source.get_coordinate(): + try: + self._update_after_move() + except: + return + #cache values + self._sink_rot = sink.get_rotation() + self._source_rot = source.get_rotation() + self._sink_coor = sink.get_coordinate() + self._source_coor = source.get_coordinate() + #draw + if self.is_highlighted(): border_color = Colors.HIGHLIGHT_COLOR + elif self.get_enabled(): border_color = Colors.CONNECTION_ENABLED_COLOR + else: border_color = Colors.CONNECTION_DISABLED_COLOR + Element.draw(self, gc, window, bg_color=None, border_color=border_color) + #draw arrow on sink port + try: + gc.set_foreground(self._arrow_color) + window.draw_polygon(gc, True, self._arrow) + except: + return diff --git a/grc/gui/Dialogs.py b/grc/gui/Dialogs.py index df424750b9..5b3b420d3b 100644 --- a/grc/gui/Dialogs.py +++ b/grc/gui/Dialogs.py @@ -23,46 +23,46 @@ import gtk import Utils class TextDisplay(gtk.TextView): - """A non editable gtk text view.""" - - def __init__(self, text=''): - """ - TextDisplay constructor. - - Args: - text: the text to display (string) - """ - text_buffer = gtk.TextBuffer() - text_buffer.set_text(text) - self.set_text = text_buffer.set_text - self.insert = lambda line: text_buffer.insert(text_buffer.get_end_iter(), line) - gtk.TextView.__init__(self, text_buffer) - self.set_editable(False) - self.set_cursor_visible(False) - self.set_wrap_mode(gtk.WRAP_WORD_CHAR) + """A non editable gtk text view.""" + + def __init__(self, text=''): + """ + TextDisplay constructor. + + Args: + text: the text to display (string) + """ + text_buffer = gtk.TextBuffer() + text_buffer.set_text(text) + self.set_text = text_buffer.set_text + self.insert = lambda line: text_buffer.insert(text_buffer.get_end_iter(), line) + gtk.TextView.__init__(self, text_buffer) + self.set_editable(False) + self.set_cursor_visible(False) + self.set_wrap_mode(gtk.WRAP_WORD_CHAR) def MessageDialogHelper(type, buttons, title=None, markup=None): - """ - Create a modal message dialog and run it. - - Args: - type: the type of message: gtk.MESSAGE_INFO, gtk.MESSAGE_WARNING, gtk.MESSAGE_QUESTION or gtk.MESSAGE_ERROR - buttons: the predefined set of buttons to use: - gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL - - Args: - tittle: the title of the window (string) - markup: the message text with pango markup - - Returns: - the gtk response from run() - """ - message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, type, buttons) - if title: message_dialog.set_title(title) - if markup: message_dialog.set_markup(markup) - response = message_dialog.run() - message_dialog.destroy() - return response + """ + Create a modal message dialog and run it. + + Args: + type: the type of message: gtk.MESSAGE_INFO, gtk.MESSAGE_WARNING, gtk.MESSAGE_QUESTION or gtk.MESSAGE_ERROR + buttons: the predefined set of buttons to use: + gtk.BUTTONS_NONE, gtk.BUTTONS_OK, gtk.BUTTONS_CLOSE, gtk.BUTTONS_CANCEL, gtk.BUTTONS_YES_NO, gtk.BUTTONS_OK_CANCEL + + Args: + tittle: the title of the window (string) + markup: the message text with pango markup + + Returns: + the gtk response from run() + """ + message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL, type, buttons) + if title: message_dialog.set_title(title) + if markup: message_dialog.set_markup(markup) + response = message_dialog.run() + message_dialog.destroy() + return response ERRORS_MARKUP_TMPL="""\ @@ -72,31 +72,31 @@ $encode($err_msg.replace('\t', ' ')) #end for""" def ErrorsDialog(flowgraph): MessageDialogHelper( - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_CLOSE, - title='Flow Graph Errors', - markup=Utils.parse_template(ERRORS_MARKUP_TMPL, errors=flowgraph.get_error_messages()), + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + title='Flow Graph Errors', + markup=Utils.parse_template(ERRORS_MARKUP_TMPL, errors=flowgraph.get_error_messages()), ) class AboutDialog(gtk.AboutDialog): - """A cute little about dialog.""" - - def __init__(self, platform): - """AboutDialog constructor.""" - gtk.AboutDialog.__init__(self) - self.set_name(platform.get_name()) - self.set_version(platform.get_version()) - self.set_license(platform.get_license()) - self.set_copyright(platform.get_license().splitlines()[0]) - self.set_website(platform.get_website()) - self.run() - self.destroy() + """A cute little about dialog.""" + + def __init__(self, platform): + """AboutDialog constructor.""" + gtk.AboutDialog.__init__(self) + self.set_name(platform.get_name()) + self.set_version(platform.get_version()) + self.set_license(platform.get_license()) + self.set_copyright(platform.get_license().splitlines()[0]) + self.set_website(platform.get_website()) + self.run() + self.destroy() def HelpDialog(): MessageDialogHelper( - type=gtk.MESSAGE_INFO, - buttons=gtk.BUTTONS_CLOSE, - title='Help', - markup="""\ + type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_CLOSE, + title='Help', + markup="""\ <b>Usage Tips</b> <u>Add block</u>: drag and drop or double click a block in the block selection window. @@ -112,15 +112,15 @@ COLORS_DIALOG_MARKUP_TMPL = """\ <b>Color Mapping</b> #if $colors - #set $max_len = max([len(color[0]) for color in $colors]) + 10 - #for $title, $color_spec in $colors + #set $max_len = max([len(color[0]) for color in $colors]) + 10 + #for $title, $color_spec in $colors <span background="$color_spec"><tt>$($encode($title).center($max_len))</tt></span> - #end for + #end for #end if """ def TypesDialog(platform): MessageDialogHelper( - type=gtk.MESSAGE_INFO, - buttons=gtk.BUTTONS_CLOSE, - title='Types', - markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors())) + type=gtk.MESSAGE_INFO, + buttons=gtk.BUTTONS_CLOSE, + title='Types', + markup=Utils.parse_template(COLORS_DIALOG_MARKUP_TMPL, colors=platform.get_colors())) diff --git a/grc/gui/DrawingArea.py b/grc/gui/DrawingArea.py index da16eb7cf2..64be4d3ea6 100644 --- a/grc/gui/DrawingArea.py +++ b/grc/gui/DrawingArea.py @@ -23,113 +23,113 @@ import gtk from Constants import MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, DND_TARGETS class DrawingArea(gtk.DrawingArea): - """ - DrawingArea is the gtk pixel map that graphical elements may draw themselves on. - The drawing area also responds to mouse and key events. - """ + """ + DrawingArea is the gtk pixel map that graphical elements may draw themselves on. + The drawing area also responds to mouse and key events. + """ - def __init__(self, flow_graph): - """ - DrawingArea contructor. - Connect event handlers. - - Args: - main_window: the main_window containing all flow graphs - """ - self.ctrl_mask = False - self._flow_graph = flow_graph - gtk.DrawingArea.__init__(self) - self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) - self.connect('realize', self._handle_window_realize) - self.connect('configure-event', self._handle_window_configure) - self.connect('expose-event', self._handle_window_expose) - self.connect('motion-notify-event', self._handle_mouse_motion) - self.connect('button-press-event', self._handle_mouse_button_press) - self.connect('button-release-event', self._handle_mouse_button_release) - self.add_events( - gtk.gdk.BUTTON_PRESS_MASK | \ - gtk.gdk.POINTER_MOTION_MASK | \ - gtk.gdk.BUTTON_RELEASE_MASK | \ - gtk.gdk.LEAVE_NOTIFY_MASK | \ - gtk.gdk.ENTER_NOTIFY_MASK - ) - #setup drag and drop - self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY) - self.connect('drag-data-received', self._handle_drag_data_received) - #setup the focus flag - self._focus_flag = False - self.get_focus_flag = lambda: self._focus_flag - def _handle_focus_event(widget, event, focus_flag): self._focus_flag = focus_flag - self.connect('leave-notify-event', _handle_focus_event, False) - self.connect('enter-notify-event', _handle_focus_event, True) + def __init__(self, flow_graph): + """ + DrawingArea contructor. + Connect event handlers. + + Args: + main_window: the main_window containing all flow graphs + """ + self.ctrl_mask = False + self._flow_graph = flow_graph + gtk.DrawingArea.__init__(self) + self.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) + self.connect('realize', self._handle_window_realize) + self.connect('configure-event', self._handle_window_configure) + self.connect('expose-event', self._handle_window_expose) + self.connect('motion-notify-event', self._handle_mouse_motion) + self.connect('button-press-event', self._handle_mouse_button_press) + self.connect('button-release-event', self._handle_mouse_button_release) + self.add_events( + gtk.gdk.BUTTON_PRESS_MASK | \ + gtk.gdk.POINTER_MOTION_MASK | \ + gtk.gdk.BUTTON_RELEASE_MASK | \ + gtk.gdk.LEAVE_NOTIFY_MASK | \ + gtk.gdk.ENTER_NOTIFY_MASK + ) + #setup drag and drop + self.drag_dest_set(gtk.DEST_DEFAULT_ALL, DND_TARGETS, gtk.gdk.ACTION_COPY) + self.connect('drag-data-received', self._handle_drag_data_received) + #setup the focus flag + self._focus_flag = False + self.get_focus_flag = lambda: self._focus_flag + def _handle_focus_event(widget, event, focus_flag): self._focus_flag = focus_flag + self.connect('leave-notify-event', _handle_focus_event, False) + self.connect('enter-notify-event', _handle_focus_event, True) - def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1) - def get_pixbuf(self): - width, height = self._pixmap.get_size() - pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) - pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height) - return pixbuf + def new_pixmap(self, width, height): return gtk.gdk.Pixmap(self.window, width, height, -1) + def get_pixbuf(self): + width, height = self._pixmap.get_size() + pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, 0, 8, width, height) + pixbuf.get_from_drawable(self._pixmap, self._pixmap.get_colormap(), 0, 0, 0, 0, width, height) + return pixbuf - ########################################################################## - ## Handlers - ########################################################################## - def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time): - """ - Handle a drag and drop by adding a block at the given coordinate. - """ - self._flow_graph.add_new_block(selection_data.data, (x, y)) + ########################################################################## + ## Handlers + ########################################################################## + def _handle_drag_data_received(self, widget, drag_context, x, y, selection_data, info, time): + """ + Handle a drag and drop by adding a block at the given coordinate. + """ + self._flow_graph.add_new_block(selection_data.data, (x, y)) - def _handle_mouse_button_press(self, widget, event): - """ - Forward button click information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_press( - double_click=(event.type == gtk.gdk._2BUTTON_PRESS), - coordinate=(event.x, event.y), - ) - if event.button == 3: self._flow_graph.handle_mouse_context_press( - coordinate=(event.x, event.y), - event=event, - ) + def _handle_mouse_button_press(self, widget, event): + """ + Forward button click information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + if event.button == 1: self._flow_graph.handle_mouse_selector_press( + double_click=(event.type == gtk.gdk._2BUTTON_PRESS), + coordinate=(event.x, event.y), + ) + if event.button == 3: self._flow_graph.handle_mouse_context_press( + coordinate=(event.x, event.y), + event=event, + ) - def _handle_mouse_button_release(self, widget, event): - """ - Forward button release information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - if event.button == 1: self._flow_graph.handle_mouse_selector_release( - coordinate=(event.x, event.y), - ) + def _handle_mouse_button_release(self, widget, event): + """ + Forward button release information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + if event.button == 1: self._flow_graph.handle_mouse_selector_release( + coordinate=(event.x, event.y), + ) - def _handle_mouse_motion(self, widget, event): - """ - Forward mouse motion information to the flow graph. - """ - self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK - self._flow_graph.handle_mouse_motion( - coordinate=(event.x, event.y), - ) + def _handle_mouse_motion(self, widget, event): + """ + Forward mouse motion information to the flow graph. + """ + self.ctrl_mask = event.state & gtk.gdk.CONTROL_MASK + self._flow_graph.handle_mouse_motion( + coordinate=(event.x, event.y), + ) - def _handle_window_realize(self, widget): - """ - Called when the window is realized. - Update the flowgraph, which calls new pixmap. - """ - self._flow_graph.update() + def _handle_window_realize(self, widget): + """ + Called when the window is realized. + Update the flowgraph, which calls new pixmap. + """ + self._flow_graph.update() - def _handle_window_configure(self, widget, event): - """ - Called when the window is resized. - Create a new pixmap for background buffer. - """ - self._pixmap = self.new_pixmap(*self.get_size_request()) + def _handle_window_configure(self, widget, event): + """ + Called when the window is resized. + Create a new pixmap for background buffer. + """ + self._pixmap = self.new_pixmap(*self.get_size_request()) - def _handle_window_expose(self, widget, event): - """ - Called when window is exposed, or queue_draw is called. - Double buffering: draw to pixmap, then draw pixmap to window. - """ - gc = self.window.new_gc() - self._flow_graph.draw(gc, self._pixmap) - self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1) + def _handle_window_expose(self, widget, event): + """ + Called when window is exposed, or queue_draw is called. + Double buffering: draw to pixmap, then draw pixmap to window. + """ + gc = self.window.new_gc() + self._flow_graph.draw(gc, self._pixmap) + self.window.draw_drawable(gc, self._pixmap, 0, 0, 0, 0, -1, -1) diff --git a/grc/gui/Element.py b/grc/gui/Element.py index eac59d88eb..915bdfb915 100644 --- a/grc/gui/Element.py +++ b/grc/gui/Element.py @@ -21,244 +21,244 @@ from Constants import LINE_SELECT_SENSITIVITY from Constants import POSSIBLE_ROTATIONS class Element(object): - """ - GraphicalElement is the base class for all graphical elements. - It contains an X,Y coordinate, a list of rectangular areas that the element occupies, - and methods to detect selection of those areas. - """ + """ + GraphicalElement is the base class for all graphical elements. + It contains an X,Y coordinate, a list of rectangular areas that the element occupies, + and methods to detect selection of those areas. + """ - def __init__(self): - """ - Make a new list of rectangular areas and lines, and set the coordinate and the rotation. - """ - self.set_rotation(POSSIBLE_ROTATIONS[0]) - self.set_coordinate((0, 0)) - self.clear() - self.set_highlighted(False) + def __init__(self): + """ + Make a new list of rectangular areas and lines, and set the coordinate and the rotation. + """ + self.set_rotation(POSSIBLE_ROTATIONS[0]) + self.set_coordinate((0, 0)) + self.clear() + self.set_highlighted(False) - def is_horizontal(self, rotation=None): - """ - Is this element horizontal? - If rotation is None, use this element's rotation. - - Args: - rotation: the optional rotation - - Returns: - true if rotation is horizontal - """ - rotation = rotation or self.get_rotation() - return rotation in (0, 180) + def is_horizontal(self, rotation=None): + """ + Is this element horizontal? + If rotation is None, use this element's rotation. + + Args: + rotation: the optional rotation + + Returns: + true if rotation is horizontal + """ + rotation = rotation or self.get_rotation() + return rotation in (0, 180) - def is_vertical(self, rotation=None): - """ - Is this element vertical? - If rotation is None, use this element's rotation. - - Args: - rotation: the optional rotation - - Returns: - true if rotation is vertical - """ - rotation = rotation or self.get_rotation() - return rotation in (90, 270) + def is_vertical(self, rotation=None): + """ + Is this element vertical? + If rotation is None, use this element's rotation. + + Args: + rotation: the optional rotation + + Returns: + true if rotation is vertical + """ + rotation = rotation or self.get_rotation() + return rotation in (90, 270) - def create_labels(self): - """ - Create labels (if applicable) and call on all children. - Call this base method before creating labels in the element. - """ - for child in self.get_children(): child.create_labels() + def create_labels(self): + """ + Create labels (if applicable) and call on all children. + Call this base method before creating labels in the element. + """ + for child in self.get_children():child.create_labels() - def create_shapes(self): - """ - Create shapes (if applicable) and call on all children. - Call this base method before creating shapes in the element. - """ - self.clear() - for child in self.get_children(): child.create_shapes() + def create_shapes(self): + """ + Create shapes (if applicable) and call on all children. + Call this base method before creating shapes in the element. + """ + self.clear() + for child in self.get_children(): child.create_shapes() - def draw(self, gc, window, border_color, bg_color): - """ - Draw in the given window. - - Args: - gc: the graphics context - window: the gtk window to draw on - border_color: the color for lines and rectangle borders - bg_color: the color for the inside of the rectangle - """ - X,Y = self.get_coordinate() - for (rX,rY),(W,H) in self._areas_list: - aX = X + rX - aY = Y + rY - gc.set_foreground(bg_color) - window.draw_rectangle(gc, True, aX, aY, W, H) - gc.set_foreground(border_color) - window.draw_rectangle(gc, False, aX, aY, W, H) - for (x1, y1),(x2, y2) in self._lines_list: - gc.set_foreground(border_color) - window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2) + def draw(self, gc, window, border_color, bg_color): + """ + Draw in the given window. + + Args: + gc: the graphics context + window: the gtk window to draw on + border_color: the color for lines and rectangle borders + bg_color: the color for the inside of the rectangle + """ + X,Y = self.get_coordinate() + for (rX,rY),(W,H) in self._areas_list: + aX = X + rX + aY = Y + rY + gc.set_foreground(bg_color) + window.draw_rectangle(gc, True, aX, aY, W, H) + gc.set_foreground(border_color) + window.draw_rectangle(gc, False, aX, aY, W, H) + for (x1, y1),(x2, y2) in self._lines_list: + gc.set_foreground(border_color) + window.draw_line(gc, X+x1, Y+y1, X+x2, Y+y2) - def rotate(self, rotation): - """ - Rotate all of the areas by 90 degrees. - - Args: - rotation: multiple of 90 degrees - """ - self.set_rotation((self.get_rotation() + rotation)%360) + def rotate(self, rotation): + """ + Rotate all of the areas by 90 degrees. + + Args: + rotation: multiple of 90 degrees + """ + self.set_rotation((self.get_rotation() + rotation)%360) - def clear(self): - """Empty the lines and areas.""" - self._areas_list = list() - self._lines_list = list() + def clear(self): + """Empty the lines and areas.""" + self._areas_list = list() + self._lines_list = list() - def set_coordinate(self, coor): - """ - Set the reference coordinate. - - Args: - coor: the coordinate tuple (x,y) - """ - self.coor = coor + def set_coordinate(self, coor): + """ + Set the reference coordinate. + + Args: + coor: the coordinate tuple (x,y) + """ + self.coor = coor - def get_parent(self): - """ - Get the parent of this element. - - Returns: - the parent - """ - return self.parent + def get_parent(self): + """ + Get the parent of this element. + + Returns: + the parent + """ + return self.parent - def set_highlighted(self, highlighted): - """ - Set the highlight status. - - Args: - highlighted: true to enable highlighting - """ - self.highlighted = highlighted + def set_highlighted(self, highlighted): + """ + Set the highlight status. + + Args: + highlighted: true to enable highlighting + """ + self.highlighted = highlighted - def is_highlighted(self): - """ - Get the highlight status. - - Returns: - true if highlighted - """ - return self.highlighted + def is_highlighted(self): + """ + Get the highlight status. + + Returns: + true if highlighted + """ + return self.highlighted - def get_coordinate(self): - """Get the coordinate. - - Returns: - the coordinate tuple (x,y) - """ - return self.coor + def get_coordinate(self): + """Get the coordinate. + + Returns: + the coordinate tuple (x,y) + """ + return self.coor - def move(self, delta_coor): - """ - Move the element by adding the delta_coor to the current coordinate. - - Args: - delta_coor: (delta_x,delta_y) tuple - """ - deltaX, deltaY = delta_coor - X, Y = self.get_coordinate() - self.set_coordinate((X+deltaX, Y+deltaY)) + def move(self, delta_coor): + """ + Move the element by adding the delta_coor to the current coordinate. + + Args: + delta_coor: (delta_x,delta_y) tuple + """ + deltaX, deltaY = delta_coor + X, Y = self.get_coordinate() + self.set_coordinate((X+deltaX, Y+deltaY)) - def add_area(self, rel_coor, area): - """ - Add an area to the area list. - An area is actually a coordinate relative to the main coordinate - with a width/height pair relative to the area coordinate. - A positive width is to the right of the coordinate. - A positive height is above the coordinate. - The area is associated with a rotation. - - Args: - rel_coor: (x,y) offset from this element's coordinate - area: (width,height) tuple - """ - self._areas_list.append((rel_coor, area)) + def add_area(self, rel_coor, area): + """ + Add an area to the area list. + An area is actually a coordinate relative to the main coordinate + with a width/height pair relative to the area coordinate. + A positive width is to the right of the coordinate. + A positive height is above the coordinate. + The area is associated with a rotation. + + Args: + rel_coor: (x,y) offset from this element's coordinate + area: (width,height) tuple + """ + self._areas_list.append((rel_coor, area)) - def add_line(self, rel_coor1, rel_coor2): - """ - Add a line to the line list. - A line is defined by 2 relative coordinates. - Lines must be horizontal or vertical. - The line is associated with a rotation. - - Args: - rel_coor1: relative (x1,y1) tuple - rel_coor2: relative (x2,y2) tuple - """ - self._lines_list.append((rel_coor1, rel_coor2)) + def add_line(self, rel_coor1, rel_coor2): + """ + Add a line to the line list. + A line is defined by 2 relative coordinates. + Lines must be horizontal or vertical. + The line is associated with a rotation. + + Args: + rel_coor1: relative (x1,y1) tuple + rel_coor2: relative (x2,y2) tuple + """ + self._lines_list.append((rel_coor1, rel_coor2)) - def what_is_selected(self, coor, coor_m=None): - """ - One coordinate specified: - Is this element selected at given coordinate? - ie: is the coordinate encompassed by one of the areas or lines? - Both coordinates specified: - Is this element within the rectangular region defined by both coordinates? - ie: do any area corners or line endpoints fall within the region? - - Args: - coor: the selection coordinate, tuple x, y - coor_m: an additional selection coordinate. - - Returns: - self if one of the areas/lines encompasses coor, else None. - """ - #function to test if p is between a and b (inclusive) - in_between = lambda p, a, b: p >= min(a, b) and p <= max(a, b) - #relative coordinate - x, y = [a-b for a,b in zip(coor, self.get_coordinate())] - if coor_m: - x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())] - #handle rectangular areas - for (x1,y1), (w,h) in self._areas_list: - if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \ - in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m): - return self - #handle horizontal or vertical lines - for (x1, y1), (x2, y2) in self._lines_list: - if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ - in_between(x2, x, x_m) and in_between(y2, y, y_m): - return self - return None - else: - #handle rectangular areas - for (x1,y1), (w,h) in self._areas_list: - if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self - #handle horizontal or vertical lines - for (x1, y1), (x2, y2) in self._lines_list: - if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY - if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY - if in_between(x, x1, x2) and in_between(y, y1, y2): return self - return None + def what_is_selected(self, coor, coor_m=None): + """ + One coordinate specified: + Is this element selected at given coordinate? + ie: is the coordinate encompassed by one of the areas or lines? + Both coordinates specified: + Is this element within the rectangular region defined by both coordinates? + ie: do any area corners or line endpoints fall within the region? + + Args: + coor: the selection coordinate, tuple x, y + coor_m: an additional selection coordinate. + + Returns: + self if one of the areas/lines encompasses coor, else None. + """ + #function to test if p is between a and b (inclusive) + in_between = lambda p, a, b: p >= min(a, b) and p <= max(a, b) + #relative coordinate + x, y = [a-b for a,b in zip(coor, self.get_coordinate())] + if coor_m: + x_m, y_m = [a-b for a,b in zip(coor_m, self.get_coordinate())] + #handle rectangular areas + for (x1,y1), (w,h) in self._areas_list: + if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x1+w, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x1, x, x_m) and in_between(y1+h, y, y_m) or \ + in_between(x1+w, x, x_m) and in_between(y1+h, y, y_m): + return self + #handle horizontal or vertical lines + for (x1, y1), (x2, y2) in self._lines_list: + if in_between(x1, x, x_m) and in_between(y1, y, y_m) or \ + in_between(x2, x, x_m) and in_between(y2, y, y_m): + return self + return None + else: + #handle rectangular areas + for (x1,y1), (w,h) in self._areas_list: + if in_between(x, x1, x1+w) and in_between(y, y1, y1+h): return self + #handle horizontal or vertical lines + for (x1, y1), (x2, y2) in self._lines_list: + if x1 == x2: x1, x2 = x1-LINE_SELECT_SENSITIVITY, x2+LINE_SELECT_SENSITIVITY + if y1 == y2: y1, y2 = y1-LINE_SELECT_SENSITIVITY, y2+LINE_SELECT_SENSITIVITY + if in_between(x, x1, x2) and in_between(y, y1, y2): return self + return None - def get_rotation(self): - """ - Get the rotation in degrees. - - Returns: - the rotation - """ - return self.rotation + def get_rotation(self): + """ + Get the rotation in degrees. + + Returns: + the rotation + """ + return self.rotation - def set_rotation(self, rotation): - """ - Set the rotation in degrees. - - Args: - rotation: the rotation""" - if rotation not in POSSIBLE_ROTATIONS: - raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS)) - self.rotation = rotation + def set_rotation(self, rotation): + """ + Set the rotation in degrees. + + Args: + rotation: the rotation""" + if rotation not in POSSIBLE_ROTATIONS: + raise Exception('"%s" is not one of the possible rotations: (%s)'%(rotation, POSSIBLE_ROTATIONS)) + self.rotation = rotation diff --git a/grc/gui/FileDialogs.py b/grc/gui/FileDialogs.py index 20172a7418..e8e859dc60 100644 --- a/grc/gui/FileDialogs.py +++ b/grc/gui/FileDialogs.py @@ -22,8 +22,8 @@ pygtk.require('2.0') import gtk from Dialogs import MessageDialogHelper from Constants import \ - DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, \ - NEW_FLOGRAPH_TITLE + DEFAULT_FILE_PATH, IMAGE_FILE_EXTENSION, \ + NEW_FLOGRAPH_TITLE import Preferences from os import path import Utils @@ -46,139 +46,139 @@ File <b>$encode($filename)</b> Does not Exist!""" ################################################## ##the filter for flow graph files def get_flow_graph_files_filter(): - filter = gtk.FileFilter() - filter.set_name('Flow Graph Files') - filter.add_pattern('*'+Preferences.file_extension()) - return filter + filter = gtk.FileFilter() + filter.set_name('Flow Graph Files') + filter.add_pattern('*'+Preferences.file_extension()) + return filter ##the filter for image files def get_image_files_filter(): - filter = gtk.FileFilter() - filter.set_name('Image Files') - filter.add_pattern('*'+IMAGE_FILE_EXTENSION) - return filter + filter = gtk.FileFilter() + filter.set_name('Image Files') + filter.add_pattern('*'+IMAGE_FILE_EXTENSION) + return filter ##the filter for all files def get_all_files_filter(): - filter = gtk.FileFilter() - filter.set_name('All Files') - filter.add_pattern('*') - return filter + filter = gtk.FileFilter() + filter.set_name('All Files') + filter.add_pattern('*') + return filter ################################################## # File Dialogs ################################################## class FileDialogHelper(gtk.FileChooserDialog): - """ - A wrapper class for the gtk file chooser dialog. - Implement a file chooser dialog with only necessary parameters. - """ - - def __init__(self, action, title): - """ - FileDialogHelper contructor. - Create a save or open dialog with cancel and ok buttons. - Use standard settings: no multiple selection, local files only, and the * filter. - - Args: - action: gtk.FILE_CHOOSER_ACTION_OPEN or gtk.FILE_CHOOSER_ACTION_SAVE - title: the title of the dialog (string) - """ - ok_stock = {gtk.FILE_CHOOSER_ACTION_OPEN : 'gtk-open', gtk.FILE_CHOOSER_ACTION_SAVE : 'gtk-save'}[action] - gtk.FileChooserDialog.__init__(self, title, None, action, ('gtk-cancel', gtk.RESPONSE_CANCEL, ok_stock, gtk.RESPONSE_OK)) - self.set_select_multiple(False) - self.set_local_only(True) - self.add_filter(get_all_files_filter()) + """ + A wrapper class for the gtk file chooser dialog. + Implement a file chooser dialog with only necessary parameters. + """ + + def __init__(self, action, title): + """ + FileDialogHelper contructor. + Create a save or open dialog with cancel and ok buttons. + Use standard settings: no multiple selection, local files only, and the * filter. + + Args: + action: gtk.FILE_CHOOSER_ACTION_OPEN or gtk.FILE_CHOOSER_ACTION_SAVE + title: the title of the dialog (string) + """ + ok_stock = {gtk.FILE_CHOOSER_ACTION_OPEN : 'gtk-open', gtk.FILE_CHOOSER_ACTION_SAVE : 'gtk-save'}[action] + gtk.FileChooserDialog.__init__(self, title, None, action, ('gtk-cancel', gtk.RESPONSE_CANCEL, ok_stock, gtk.RESPONSE_OK)) + self.set_select_multiple(False) + self.set_local_only(True) + self.add_filter(get_all_files_filter()) class FileDialog(FileDialogHelper): - """A dialog box to save or open flow graph files. This is a base class, do not use.""" - - def __init__(self, current_file_path=''): - """ - FileDialog constructor. - - Args: - current_file_path: the current directory or path to the open flow graph - """ - if not current_file_path: current_file_path = path.join(DEFAULT_FILE_PATH, NEW_FLOGRAPH_TITLE + Preferences.file_extension()) - if self.type == OPEN_FLOW_GRAPH: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_OPEN, 'Open a Flow Graph from a File...') - self.add_and_set_filter(get_flow_graph_files_filter()) - self.set_select_multiple(True) - elif self.type == SAVE_FLOW_GRAPH: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...') - self.add_and_set_filter(get_flow_graph_files_filter()) - self.set_current_name(path.basename(current_file_path)) #show the current filename - elif self.type == SAVE_IMAGE: - FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph Screen Shot...') - self.add_and_set_filter(get_image_files_filter()) - current_file_path = current_file_path + IMAGE_FILE_EXTENSION - self.set_current_name(path.basename(current_file_path)) #show the current filename - self.set_current_folder(path.dirname(current_file_path)) #current directory - - def add_and_set_filter(self, filter): - """ - Add the gtk file filter to the list of filters and set it as the default file filter. - - Args: - filter: a gtk file filter. - """ - self.add_filter(filter) - self.set_filter(filter) - - def get_rectified_filename(self): - """ - Run the dialog and get the filename. - If this is a save dialog and the file name is missing the extension, append the file extension. - If the file name with the extension already exists, show a overwrite dialog. - If this is an open dialog, return a list of filenames. - - Returns: - the complete file path - """ - if gtk.FileChooserDialog.run(self) != gtk.RESPONSE_OK: return None #response was cancel - ############################################# - # Handle Save Dialogs - ############################################# - if self.type in (SAVE_FLOW_GRAPH, SAVE_IMAGE): - filename = self.get_filename() - extension = { - SAVE_FLOW_GRAPH: Preferences.file_extension(), - SAVE_IMAGE: IMAGE_FILE_EXTENSION, - }[self.type] - #append the missing file extension if the filter matches - if path.splitext(filename)[1].lower() != extension: filename += extension - self.set_current_name(path.basename(filename)) #show the filename with extension - if path.exists(filename): #ask the user to confirm overwrite - if MessageDialogHelper( - gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Confirm Overwrite!', - Utils.parse_template(FILE_OVERWRITE_MARKUP_TMPL, filename=filename), - ) == gtk.RESPONSE_NO: return self.get_rectified_filename() - return filename - ############################################# - # Handle Open Dialogs - ############################################# - elif self.type in (OPEN_FLOW_GRAPH,): - filenames = self.get_filenames() - for filename in filenames: - if not path.exists(filename): #show a warning and re-run - MessageDialogHelper( - gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, 'Cannot Open!', - Utils.parse_template(FILE_DNE_MARKUP_TMPL, filename=filename), - ) - return self.get_rectified_filename() - return filenames - - def run(self): - """ - Get the filename and destroy the dialog. - - Returns: - the filename or None if a close/cancel occured. - """ - filename = self.get_rectified_filename() - self.destroy() - return filename + """A dialog box to save or open flow graph files. This is a base class, do not use.""" + + def __init__(self, current_file_path=''): + """ + FileDialog constructor. + + Args: + current_file_path: the current directory or path to the open flow graph + """ + if not current_file_path: current_file_path = path.join(DEFAULT_FILE_PATH, NEW_FLOGRAPH_TITLE + Preferences.file_extension()) + if self.type == OPEN_FLOW_GRAPH: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_OPEN, 'Open a Flow Graph from a File...') + self.add_and_set_filter(get_flow_graph_files_filter()) + self.set_select_multiple(True) + elif self.type == SAVE_FLOW_GRAPH: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph to a File...') + self.add_and_set_filter(get_flow_graph_files_filter()) + self.set_current_name(path.basename(current_file_path)) #show the current filename + elif self.type == SAVE_IMAGE: + FileDialogHelper.__init__(self, gtk.FILE_CHOOSER_ACTION_SAVE, 'Save a Flow Graph Screen Shot...') + self.add_and_set_filter(get_image_files_filter()) + current_file_path = current_file_path + IMAGE_FILE_EXTENSION + self.set_current_name(path.basename(current_file_path)) #show the current filename + self.set_current_folder(path.dirname(current_file_path)) #current directory + + def add_and_set_filter(self, filter): + """ + Add the gtk file filter to the list of filters and set it as the default file filter. + + Args: + filter: a gtk file filter. + """ + self.add_filter(filter) + self.set_filter(filter) + + def get_rectified_filename(self): + """ + Run the dialog and get the filename. + If this is a save dialog and the file name is missing the extension, append the file extension. + If the file name with the extension already exists, show a overwrite dialog. + If this is an open dialog, return a list of filenames. + + Returns: + the complete file path + """ + if gtk.FileChooserDialog.run(self) != gtk.RESPONSE_OK: return None #response was cancel + ############################################# + # Handle Save Dialogs + ############################################# + if self.type in (SAVE_FLOW_GRAPH, SAVE_IMAGE): + filename = self.get_filename() + extension = { + SAVE_FLOW_GRAPH: Preferences.file_extension(), + SAVE_IMAGE: IMAGE_FILE_EXTENSION, + }[self.type] + #append the missing file extension if the filter matches + if path.splitext(filename)[1].lower() != extension: filename += extension + self.set_current_name(path.basename(filename)) #show the filename with extension + if path.exists(filename): #ask the user to confirm overwrite + if MessageDialogHelper( + gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Confirm Overwrite!', + Utils.parse_template(FILE_OVERWRITE_MARKUP_TMPL, filename=filename), + ) == gtk.RESPONSE_NO: return self.get_rectified_filename() + return filename + ############################################# + # Handle Open Dialogs + ############################################# + elif self.type in (OPEN_FLOW_GRAPH,): + filenames = self.get_filenames() + for filename in filenames: + if not path.exists(filename): #show a warning and re-run + MessageDialogHelper( + gtk.MESSAGE_WARNING, gtk.BUTTONS_CLOSE, 'Cannot Open!', + Utils.parse_template(FILE_DNE_MARKUP_TMPL, filename=filename), + ) + return self.get_rectified_filename() + return filenames + + def run(self): + """ + Get the filename and destroy the dialog. + + Returns: + the filename or None if a close/cancel occured. + """ + filename = self.get_rectified_filename() + self.destroy() + return filename class OpenFlowGraphFileDialog(FileDialog): type = OPEN_FLOW_GRAPH class SaveFlowGraphFileDialog(FileDialog): type = SAVE_FLOW_GRAPH diff --git a/grc/gui/FlowGraph.py b/grc/gui/FlowGraph.py index 3f17c47bc8..4dff675afb 100644 --- a/grc/gui/FlowGraph.py +++ b/grc/gui/FlowGraph.py @@ -29,536 +29,537 @@ import random import Messages class FlowGraph(Element): - """ - FlowGraph is the data structure to store graphical signal blocks, - graphical inputs and outputs, - and the connections between inputs and outputs. - """ - - def __init__(self): - """ - FlowGraph contructor. - Create a list for signal blocks and connections. Connect mouse handlers. - """ - Element.__init__(self) - #when is the flow graph selected? (used by keyboard event handler) - self.is_selected = lambda: bool(self.get_selected_elements()) - #important vars dealing with mouse event tracking - self.element_moved = False - self.mouse_pressed = False - self.unselect() - self.press_coor = (0, 0) - #selected ports - self._old_selected_port = None - self._new_selected_port = None - #context menu - self._context_menu = gtk.Menu() - for action in [ - Actions.BLOCK_CUT, - Actions.BLOCK_COPY, - Actions.BLOCK_PASTE, - Actions.ELEMENT_DELETE, - Actions.BLOCK_ROTATE_CCW, - Actions.BLOCK_ROTATE_CW, - Actions.BLOCK_ENABLE, - Actions.BLOCK_DISABLE, - Actions.BLOCK_PARAM_MODIFY, - Actions.BLOCK_CREATE_HIER, - Actions.OPEN_HIER, - ]: self._context_menu.append(action.create_menu_item()) - - ########################################################################### - # Access Drawing Area - ########################################################################### - def get_drawing_area(self): return self.drawing_area - def queue_draw(self): self.get_drawing_area().queue_draw() - def get_size(self): return self.get_drawing_area().get_size_request() - def set_size(self, *args): self.get_drawing_area().set_size_request(*args) - def get_scroll_pane(self): return self.drawing_area.get_parent() - def get_ctrl_mask(self): return self.drawing_area.ctrl_mask - def new_pixmap(self, *args): return self.get_drawing_area().new_pixmap(*args) - - def add_new_block(self, key, coor=None): - """ - Add a block of the given key to this flow graph. - - Args: - key: the block key - coor: an optional coordinate or None for random - """ - id = self._get_unique_id(key) - #calculate the position coordinate - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - if coor is None: coor = ( - int(random.uniform(.25, .75)*h_adj.page_size + h_adj.get_value()), - int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()), - ) - #get the new block - block = self.get_new_block(key) - block.set_coordinate(coor) - block.set_rotation(0) - block.get_param('id').set_value(id) - Actions.ELEMENT_CREATE() - - return id - - ########################################################################### - # Copy Paste - ########################################################################### - def copy_to_clipboard(self): - """ - Copy the selected blocks and connections into the clipboard. - - Returns: - the clipboard - """ - #get selected blocks - blocks = self.get_selected_blocks() - if not blocks: return None - #calc x and y min - x_min, y_min = blocks[0].get_coordinate() - for block in blocks: - x, y = block.get_coordinate() - x_min = min(x, x_min) - y_min = min(y, y_min) - #get connections between selected blocks - connections = filter( - lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, - self.get_connections(), - ) - clipboard = ( - (x_min, y_min), - [block.export_data() for block in blocks], - [connection.export_data() for connection in connections], - ) - return clipboard - - def paste_from_clipboard(self, clipboard): - """ - Paste the blocks and connections from the clipboard. - - Args: - clipboard: the nested data of blocks, connections - """ - selected = set() - (x_min, y_min), blocks_n, connections_n = clipboard - old_id2block = dict() - #recalc the position - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - x_off = h_adj.get_value() - x_min + h_adj.page_size/4 - y_off = v_adj.get_value() - y_min + v_adj.page_size/4 - #create blocks - for block_n in blocks_n: - block_key = block_n.find('key') - if block_key == 'options': continue - block = self.get_new_block(block_key) - selected.add(block) - #set params - params_n = block_n.findall('param') - for param_n in params_n: - param_key = param_n.find('key') - param_value = param_n.find('value') - #setup id parameter - if param_key == 'id': - old_id2block[param_value] = block - #if the block id is not unique, get a new block id - if param_value in [block.get_id() for block in self.get_blocks()]: - param_value = self._get_unique_id(param_value) - #set value to key - block.get_param(param_key).set_value(param_value) - #move block to offset coordinate - block.move((x_off, y_off)) - #update before creating connections - self.update() - #create connections - for connection_n in connections_n: - source = old_id2block[connection_n.find('source_block_id')].get_source(connection_n.find('source_key')) - sink = old_id2block[connection_n.find('sink_block_id')].get_sink(connection_n.find('sink_key')) - self.connect(source, sink) - #set all pasted elements selected - for block in selected: selected = selected.union(set(block.get_connections())) - self._selected_elements = list(selected) - - ########################################################################### - # Modify Selected - ########################################################################### - def type_controller_modify_selected(self, direction): - """ - Change the registered type controller for the selected signal blocks. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - return any([sb.type_controller_modify(direction) for sb in self.get_selected_blocks()]) - - def port_controller_modify_selected(self, direction): - """ - Change port controller for the selected signal blocks. - - Args: - direction: +1 or -1 - - Returns: - true for changed - """ - return any([sb.port_controller_modify(direction) for sb in self.get_selected_blocks()]) - - def enable_selected(self, enable): - """ - Enable/disable the selected blocks. - - Args: - enable: true to enable - - Returns: - true if changed - """ - changed = False - for selected_block in self.get_selected_blocks(): - if selected_block.get_enabled() != enable: - selected_block.set_enabled(enable) - changed = True - return changed - - def move_selected(self, delta_coordinate): - """ - Move the element and by the change in coordinates. - - Args: - delta_coordinate: the change in coordinates - """ - for selected_block in self.get_selected_blocks(): - selected_block.move(delta_coordinate) - self.element_moved = True - - def rotate_selected(self, rotation): - """ - Rotate the selected blocks by multiples of 90 degrees. - - Args: - rotation: the rotation in degrees - - Returns: - true if changed, otherwise false. - """ - if not self.get_selected_blocks(): return False - #initialize min and max coordinates - min_x, min_y = self.get_selected_block().get_coordinate() - max_x, max_y = self.get_selected_block().get_coordinate() - #rotate each selected block, and find min/max coordinate - for selected_block in self.get_selected_blocks(): - selected_block.rotate(rotation) - #update the min/max coordinate - x, y = selected_block.get_coordinate() - min_x, min_y = min(min_x, x), min(min_y, y) - max_x, max_y = max(max_x, x), max(max_y, y) - #calculate center point of slected blocks - ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2 - #rotate the blocks around the center point - for selected_block in self.get_selected_blocks(): - x, y = selected_block.get_coordinate() - x, y = Utils.get_rotated_coordinate((x - ctr_x, y - ctr_y), rotation) - selected_block.set_coordinate((x + ctr_x, y + ctr_y)) - return True - - def remove_selected(self): - """ - Remove selected elements - - Returns: - true if changed. - """ - changed = False - for selected_element in self.get_selected_elements(): - self.remove_element(selected_element) - changed = True - return changed - - def draw(self, gc, window): - """ - Draw the background and grid if enabled. - Draw all of the elements in this flow graph onto the pixmap. - Draw the pixmap to the drawable window of this flow graph. - """ - W,H = self.get_size() - #draw the background - gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR) - window.draw_rectangle(gc, True, 0, 0, W, H) - #draw multi select rectangle - if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()): - #coordinates - x1, y1 = self.press_coor - x2, y2 = self.get_coordinate() - #calculate top-left coordinate and width/height - x, y = int(min(x1, x2)), int(min(y1, y2)) - w, h = int(abs(x1 - x2)), int(abs(y1 - y2)) - #draw - gc.set_foreground(Colors.HIGHLIGHT_COLOR) - window.draw_rectangle(gc, True, x, y, w, h) - gc.set_foreground(Colors.BORDER_COLOR) - window.draw_rectangle(gc, False, x, y, w, h) - #draw blocks on top of connections - for element in self.get_connections() + self.get_blocks(): - element.draw(gc, window) - #draw selected blocks on top of selected connections - for selected_element in self.get_selected_connections() + self.get_selected_blocks(): - selected_element.draw(gc, window) - - def update_selected(self): - """ - Remove deleted elements from the selected elements list. - Update highlighting so only the selected are highlighted. - """ - selected_elements = self.get_selected_elements() - elements = self.get_elements() - #remove deleted elements - for selected in selected_elements: - if selected in elements: continue - selected_elements.remove(selected) - if self._old_selected_port and self._old_selected_port.get_parent() not in elements: - self._old_selected_port = None - if self._new_selected_port and self._new_selected_port.get_parent() not in elements: - self._new_selected_port = None - #update highlighting - for element in elements: - element.set_highlighted(element in selected_elements) - - def update(self): - """ - Call the top level rewrite and validate. - Call the top level create labels and shapes. - """ - self.rewrite() - self.validate() - self.create_labels() - self.create_shapes() - - ########################################################################## - ## Get Selected - ########################################################################## - def unselect(self): - """ - Set selected elements to an empty set. - """ - self._selected_elements = [] - - def what_is_selected(self, coor, coor_m=None): - """ - What is selected? - At the given coordinate, return the elements found to be selected. - If coor_m is unspecified, return a list of only the first element found to be selected: - Iterate though the elements backwards since top elements are at the end of the list. - If an element is selected, place it at the end of the list so that is is drawn last, - and hence on top. Update the selected port information. - - Args: - coor: the coordinate of the mouse click - coor_m: the coordinate for multi select - - Returns: - the selected blocks and connections or an empty list - """ - selected_port = None - selected = set() - #check the elements - for element in reversed(self.get_elements()): - selected_element = element.what_is_selected(coor, coor_m) - if not selected_element: continue - #update the selected port information - if selected_element.is_port(): - if not coor_m: selected_port = selected_element - selected_element = selected_element.get_parent() - selected.add(selected_element) - #place at the end of the list - self.get_elements().remove(element) - self.get_elements().append(element) - #single select mode, break - if not coor_m: break - #update selected ports - self._old_selected_port = self._new_selected_port - self._new_selected_port = selected_port - return list(selected) - - def get_selected_connections(self): - """ - Get a group of selected connections. - - Returns: - sub set of connections in this flow graph - """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_connection(): selected.add(selected_element) - return list(selected) - - def get_selected_blocks(self): - """ - Get a group of selected blocks. - - Returns: - sub set of blocks in this flow graph - """ - selected = set() - for selected_element in self.get_selected_elements(): - if selected_element.is_block(): selected.add(selected_element) - return list(selected) - - def get_selected_block(self): - """ - Get the selected block when a block or port is selected. - - Returns: - a block or None - """ - return self.get_selected_blocks() and self.get_selected_blocks()[0] or None - - def get_selected_elements(self): - """ - Get the group of selected elements. - - Returns: - sub set of elements in this flow graph - """ - return self._selected_elements - - def get_selected_element(self): - """ - Get the selected element. - - Returns: - a block, port, or connection or None - """ - return self.get_selected_elements() and self.get_selected_elements()[0] or None - - def update_selected_elements(self): - """ - Update the selected elements. - The update behavior depends on the state of the mouse button. - When the mouse button pressed the selection will change when - the control mask is set or the new selection is not in the current group. - When the mouse button is released the selection will change when - the mouse has moved and the control mask is set or the current group is empty. - Attempt to make a new connection if the old and ports are filled. - If the control mask is set, merge with the current elements. - """ - selected_elements = None - if self.mouse_pressed: - new_selections = self.what_is_selected(self.get_coordinate()) - #update the selections if the new selection is not in the current selections - #allows us to move entire selected groups of elements - if self.get_ctrl_mask() or not ( - new_selections and new_selections[0] in self.get_selected_elements() - ): selected_elements = new_selections - else: #called from a mouse release - if not self.element_moved and (not self.get_selected_elements() or self.get_ctrl_mask()): - selected_elements = self.what_is_selected(self.get_coordinate(), self.press_coor) - #this selection and the last were ports, try to connect them - if self._old_selected_port and self._new_selected_port and \ - self._old_selected_port is not self._new_selected_port: - try: - self.connect(self._old_selected_port, self._new_selected_port) - Actions.ELEMENT_CREATE() - except: Messages.send_fail_connection() - self._old_selected_port = None - self._new_selected_port = None - return - #update selected elements - if selected_elements is None: return - old_elements = set(self.get_selected_elements()) - self._selected_elements = list(set(selected_elements)) - new_elements = set(self.get_selected_elements()) - #if ctrl, set the selected elements to the union - intersection of old and new - if self.get_ctrl_mask(): - self._selected_elements = list( - set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) - ) - Actions.ELEMENT_SELECT() - - ########################################################################## - ## Event Handlers - ########################################################################## - def handle_mouse_context_press(self, coordinate, event): - """ - The context mouse button was pressed: - If no elements were selected, perform re-selection at this coordinate. - Then, show the context menu at the mouse click location. - """ - selections = self.what_is_selected(coordinate) - if not set(selections).intersection(self.get_selected_elements()): - self.set_coordinate(coordinate) - self.mouse_pressed = True - self.update_selected_elements() - self.mouse_pressed = False - self._context_menu.popup(None, None, None, event.button, event.time) - - def handle_mouse_selector_press(self, double_click, coordinate): - """ - The selector mouse button was pressed: - Find the selected element. Attempt a new connection if possible. - Open the block params window on a double click. - Update the selection state of the flow graph. - """ - self.press_coor = coordinate - self.set_coordinate(coordinate) - self.time = 0 - self.mouse_pressed = True - if double_click: self.unselect() - self.update_selected_elements() - #double click detected, bring up params dialog if possible - if double_click and self.get_selected_block(): - self.mouse_pressed = False - Actions.BLOCK_PARAM_MODIFY() - - def handle_mouse_selector_release(self, coordinate): - """ - The selector mouse button was released: - Update the state, handle motion (dragging). - And update the selected flowgraph elements. - """ - self.set_coordinate(coordinate) - self.time = 0 - self.mouse_pressed = False - if self.element_moved: - Actions.BLOCK_MOVE() - self.element_moved = False - self.update_selected_elements() - - def handle_mouse_motion(self, coordinate): - """ - The mouse has moved, respond to mouse dragging. - Move a selected element to the new coordinate. - Auto-scroll the scroll bars at the boundaries. - """ - #to perform a movement, the mouse must be pressed - # (no longer checking pending events via gtk.events_pending() - always true in Windows) - if not self.mouse_pressed: return - #perform autoscrolling - width, height = self.get_size() - x, y = coordinate - h_adj = self.get_scroll_pane().get_hadjustment() - v_adj = self.get_scroll_pane().get_vadjustment() - for pos, length, adj, adj_val, adj_len in ( - (x, width, h_adj, h_adj.get_value(), h_adj.page_size), - (y, height, v_adj, v_adj.get_value(), v_adj.page_size), - ): - #scroll if we moved near the border - if pos-adj_val > adj_len-SCROLL_PROXIMITY_SENSITIVITY and adj_val+SCROLL_DISTANCE < length-adj_len: - adj.set_value(adj_val+SCROLL_DISTANCE) - adj.emit('changed') - elif pos-adj_val < SCROLL_PROXIMITY_SENSITIVITY: - adj.set_value(adj_val-SCROLL_DISTANCE) - adj.emit('changed') - #remove the connection if selected in drag event - if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): - Actions.ELEMENT_DELETE() - #move the selected elements and record the new coordinate - X, Y = self.get_coordinate() - if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y))) - self.set_coordinate((x, y)) - #queue draw for animation - self.queue_draw() + """ + FlowGraph is the data structure to store graphical signal blocks, + graphical inputs and outputs, + and the connections between inputs and outputs. + """ + + def __init__(self): + """ + FlowGraph contructor. + Create a list for signal blocks and connections. Connect mouse handlers. + """ + Element.__init__(self) + #when is the flow graph selected? (used by keyboard event handler) + self.is_selected = lambda: bool(self.get_selected_elements()) + #important vars dealing with mouse event tracking + self.element_moved = False + self.mouse_pressed = False + self.unselect() + self.press_coor = (0, 0) + #selected ports + self._old_selected_port = None + self._new_selected_port = None + #context menu + self._context_menu = gtk.Menu() + for action in [ + Actions.BLOCK_CUT, + Actions.BLOCK_COPY, + Actions.BLOCK_PASTE, + Actions.ELEMENT_DELETE, + Actions.BLOCK_ROTATE_CCW, + Actions.BLOCK_ROTATE_CW, + Actions.BLOCK_ENABLE, + Actions.BLOCK_DISABLE, + Actions.BLOCK_PARAM_MODIFY, + Actions.BLOCK_CREATE_HIER, + Actions.OPEN_HIER, + Actions.BUSSIFY_SOURCES, + Actions.BUSSIFY_SINKS, + ]: self._context_menu.append(action.create_menu_item()) + + ########################################################################### + # Access Drawing Area + ########################################################################### + def get_drawing_area(self): return self.drawing_area + def queue_draw(self): self.get_drawing_area().queue_draw() + def get_size(self): return self.get_drawing_area().get_size_request() + def set_size(self, *args): self.get_drawing_area().set_size_request(*args) + def get_scroll_pane(self): return self.drawing_area.get_parent() + def get_ctrl_mask(self): return self.drawing_area.ctrl_mask + def new_pixmap(self, *args): return self.get_drawing_area().new_pixmap(*args) + + def add_new_block(self, key, coor=None): + """ + Add a block of the given key to this flow graph. + + Args: + key: the block key + coor: an optional coordinate or None for random + """ + id = self._get_unique_id(key) + #calculate the position coordinate + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + if coor is None: coor = ( + int(random.uniform(.25, .75)*h_adj.page_size + h_adj.get_value()), + int(random.uniform(.25, .75)*v_adj.page_size + v_adj.get_value()), + ) + #get the new block + block = self.get_new_block(key) + block.set_coordinate(coor) + block.set_rotation(0) + block.get_param('id').set_value(id) + Actions.ELEMENT_CREATE() + return id + + ########################################################################### + # Copy Paste + ########################################################################### + def copy_to_clipboard(self): + """ + Copy the selected blocks and connections into the clipboard. + + Returns: + the clipboard + """ + #get selected blocks + blocks = self.get_selected_blocks() + if not blocks: return None + #calc x and y min + x_min, y_min = blocks[0].get_coordinate() + for block in blocks: + x, y = block.get_coordinate() + x_min = min(x, x_min) + y_min = min(y, y_min) + #get connections between selected blocks + connections = filter( + lambda c: c.get_source().get_parent() in blocks and c.get_sink().get_parent() in blocks, + self.get_connections(), + ) + clipboard = ( + (x_min, y_min), + [block.export_data() for block in blocks], + [connection.export_data() for connection in connections], + ) + return clipboard + + def paste_from_clipboard(self, clipboard): + """ + Paste the blocks and connections from the clipboard. + + Args: + clipboard: the nested data of blocks, connections + """ + selected = set() + (x_min, y_min), blocks_n, connections_n = clipboard + old_id2block = dict() + #recalc the position + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + x_off = h_adj.get_value() - x_min + h_adj.page_size/4 + y_off = v_adj.get_value() - y_min + v_adj.page_size/4 + #create blocks + for block_n in blocks_n: + block_key = block_n.find('key') + if block_key == 'options': continue + block = self.get_new_block(block_key) + selected.add(block) + #set params + params_n = block_n.findall('param') + for param_n in params_n: + param_key = param_n.find('key') + param_value = param_n.find('value') + #setup id parameter + if param_key == 'id': + old_id2block[param_value] = block + #if the block id is not unique, get a new block id + if param_value in [bluck.get_id() for bluck in self.get_blocks()]: + param_value = self._get_unique_id(param_value) + #set value to key + block.get_param(param_key).set_value(param_value) + #move block to offset coordinate + block.move((x_off, y_off)) + #update before creating connections + self.update() + #create connections + for connection_n in connections_n: + source = old_id2block[connection_n.find('source_block_id')].get_source(connection_n.find('source_key')) + sink = old_id2block[connection_n.find('sink_block_id')].get_sink(connection_n.find('sink_key')) + self.connect(source, sink) + #set all pasted elements selected + for block in selected: selected = selected.union(set(block.get_connections())) + self._selected_elements = list(selected) + + ########################################################################### + # Modify Selected + ########################################################################### + def type_controller_modify_selected(self, direction): + """ + Change the registered type controller for the selected signal blocks. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + return any([sb.type_controller_modify(direction) for sb in self.get_selected_blocks()]) + + def port_controller_modify_selected(self, direction): + """ + Change port controller for the selected signal blocks. + + Args: + direction: +1 or -1 + + Returns: + true for changed + """ + return any([sb.port_controller_modify(direction) for sb in self.get_selected_blocks()]) + + def enable_selected(self, enable): + """ + Enable/disable the selected blocks. + + Args: + enable: true to enable + + Returns: + true if changed + """ + changed = False + for selected_block in self.get_selected_blocks(): + if selected_block.get_enabled() != enable: + selected_block.set_enabled(enable) + changed = True + return changed + + def move_selected(self, delta_coordinate): + """ + Move the element and by the change in coordinates. + + Args: + delta_coordinate: the change in coordinates + """ + for selected_block in self.get_selected_blocks(): + selected_block.move(delta_coordinate) + self.element_moved = True + + def rotate_selected(self, rotation): + """ + Rotate the selected blocks by multiples of 90 degrees. + + Args: + rotation: the rotation in degrees + + Returns: + true if changed, otherwise false. + """ + if not self.get_selected_blocks(): return False + #initialize min and max coordinates + min_x, min_y = self.get_selected_block().get_coordinate() + max_x, max_y = self.get_selected_block().get_coordinate() + #rotate each selected block, and find min/max coordinate + for selected_block in self.get_selected_blocks(): + selected_block.rotate(rotation) + #update the min/max coordinate + x, y = selected_block.get_coordinate() + min_x, min_y = min(min_x, x), min(min_y, y) + max_x, max_y = max(max_x, x), max(max_y, y) + #calculate center point of slected blocks + ctr_x, ctr_y = (max_x + min_x)/2, (max_y + min_y)/2 + #rotate the blocks around the center point + for selected_block in self.get_selected_blocks(): + x, y = selected_block.get_coordinate() + x, y = Utils.get_rotated_coordinate((x - ctr_x, y - ctr_y), rotation) + selected_block.set_coordinate((x + ctr_x, y + ctr_y)) + return True + + def remove_selected(self): + """ + Remove selected elements + + Returns: + true if changed. + """ + changed = False + for selected_element in self.get_selected_elements(): + self.remove_element(selected_element) + changed = True + return changed + + def draw(self, gc, window): + """ + Draw the background and grid if enabled. + Draw all of the elements in this flow graph onto the pixmap. + Draw the pixmap to the drawable window of this flow graph. + """ + W,H = self.get_size() + #draw the background + gc.set_foreground(Colors.FLOWGRAPH_BACKGROUND_COLOR) + window.draw_rectangle(gc, True, 0, 0, W, H) + #draw multi select rectangle + if self.mouse_pressed and (not self.get_selected_elements() or self.get_ctrl_mask()): + #coordinates + x1, y1 = self.press_coor + x2, y2 = self.get_coordinate() + #calculate top-left coordinate and width/height + x, y = int(min(x1, x2)), int(min(y1, y2)) + w, h = int(abs(x1 - x2)), int(abs(y1 - y2)) + #draw + gc.set_foreground(Colors.HIGHLIGHT_COLOR) + window.draw_rectangle(gc, True, x, y, w, h) + gc.set_foreground(Colors.BORDER_COLOR) + window.draw_rectangle(gc, False, x, y, w, h) + #draw blocks on top of connections + for element in self.get_connections() + self.get_blocks(): + element.draw(gc, window) + #draw selected blocks on top of selected connections + for selected_element in self.get_selected_connections() + self.get_selected_blocks(): + selected_element.draw(gc, window) + + def update_selected(self): + """ + Remove deleted elements from the selected elements list. + Update highlighting so only the selected are highlighted. + """ + selected_elements = self.get_selected_elements() + elements = self.get_elements() + #remove deleted elements + for selected in selected_elements: + if selected in elements: continue + selected_elements.remove(selected) + if self._old_selected_port and self._old_selected_port.get_parent() not in elements: + self._old_selected_port = None + if self._new_selected_port and self._new_selected_port.get_parent() not in elements: + self._new_selected_port = None + #update highlighting + for element in elements: + element.set_highlighted(element in selected_elements) + + def update(self): + """ + Call the top level rewrite and validate. + Call the top level create labels and shapes. + """ + self.rewrite() + self.validate() + self.create_labels() + self.create_shapes() + + ########################################################################## + ## Get Selected + ########################################################################## + def unselect(self): + """ + Set selected elements to an empty set. + """ + self._selected_elements = [] + + def what_is_selected(self, coor, coor_m=None): + """ + What is selected? + At the given coordinate, return the elements found to be selected. + If coor_m is unspecified, return a list of only the first element found to be selected: + Iterate though the elements backwards since top elements are at the end of the list. + If an element is selected, place it at the end of the list so that is is drawn last, + and hence on top. Update the selected port information. + + Args: + coor: the coordinate of the mouse click + coor_m: the coordinate for multi select + + Returns: + the selected blocks and connections or an empty list + """ + selected_port = None + selected = set() + #check the elements + for element in reversed(self.get_elements()): + selected_element = element.what_is_selected(coor, coor_m) + if not selected_element: continue + #update the selected port information + if selected_element.is_port(): + if not coor_m: selected_port = selected_element + selected_element = selected_element.get_parent() + selected.add(selected_element) + #place at the end of the list + self.get_elements().remove(element) + self.get_elements().append(element) + #single select mode, break + if not coor_m: break + #update selected ports + self._old_selected_port = self._new_selected_port + self._new_selected_port = selected_port + return list(selected) + + def get_selected_connections(self): + """ + Get a group of selected connections. + + Returns: + sub set of connections in this flow graph + """ + selected = set() + for selected_element in self.get_selected_elements(): + if selected_element.is_connection(): selected.add(selected_element) + return list(selected) + + def get_selected_blocks(self): + """ + Get a group of selected blocks. + + Returns: + sub set of blocks in this flow graph + """ + selected = set() + for selected_element in self.get_selected_elements(): + if selected_element.is_block(): selected.add(selected_element) + return list(selected) + + def get_selected_block(self): + """ + Get the selected block when a block or port is selected. + + Returns: + a block or None + """ + return self.get_selected_blocks() and self.get_selected_blocks()[0] or None + + def get_selected_elements(self): + """ + Get the group of selected elements. + + Returns: + sub set of elements in this flow graph + """ + return self._selected_elements + + def get_selected_element(self): + """ + Get the selected element. + + Returns: + a block, port, or connection or None + """ + return self.get_selected_elements() and self.get_selected_elements()[0] or None + + def update_selected_elements(self): + """ + Update the selected elements. + The update behavior depends on the state of the mouse button. + When the mouse button pressed the selection will change when + the control mask is set or the new selection is not in the current group. + When the mouse button is released the selection will change when + the mouse has moved and the control mask is set or the current group is empty. + Attempt to make a new connection if the old and ports are filled. + If the control mask is set, merge with the current elements. + """ + selected_elements = None + if self.mouse_pressed: + new_selections = self.what_is_selected(self.get_coordinate()) + #update the selections if the new selection is not in the current selections + #allows us to move entire selected groups of elements + if self.get_ctrl_mask() or not ( + new_selections and new_selections[0] in self.get_selected_elements() + ): selected_elements = new_selections + else: #called from a mouse release + if not self.element_moved and (not self.get_selected_elements() or self.get_ctrl_mask()): + selected_elements = self.what_is_selected(self.get_coordinate(), self.press_coor) + #this selection and the last were ports, try to connect them + if self._old_selected_port and self._new_selected_port and \ + self._old_selected_port is not self._new_selected_port: + try: + self.connect(self._old_selected_port, self._new_selected_port) + Actions.ELEMENT_CREATE() + except: Messages.send_fail_connection() + self._old_selected_port = None + self._new_selected_port = None + return + #update selected elements + if selected_elements is None: return + old_elements = set(self.get_selected_elements()) + self._selected_elements = list(set(selected_elements)) + new_elements = set(self.get_selected_elements()) + #if ctrl, set the selected elements to the union - intersection of old and new + if self.get_ctrl_mask(): + self._selected_elements = list( + set.union(old_elements, new_elements) - set.intersection(old_elements, new_elements) + ) + Actions.ELEMENT_SELECT() + + ########################################################################## + ## Event Handlers + ########################################################################## + def handle_mouse_context_press(self, coordinate, event): + """ + The context mouse button was pressed: + If no elements were selected, perform re-selection at this coordinate. + Then, show the context menu at the mouse click location. + """ + selections = self.what_is_selected(coordinate) + if not set(selections).intersection(self.get_selected_elements()): + self.set_coordinate(coordinate) + self.mouse_pressed = True + self.update_selected_elements() + self.mouse_pressed = False + self._context_menu.popup(None, None, None, event.button, event.time) + + def handle_mouse_selector_press(self, double_click, coordinate): + """ + The selector mouse button was pressed: + Find the selected element. Attempt a new connection if possible. + Open the block params window on a double click. + Update the selection state of the flow graph. + """ + self.press_coor = coordinate + self.set_coordinate(coordinate) + self.time = 0 + self.mouse_pressed = True + if double_click: self.unselect() + self.update_selected_elements() + #double click detected, bring up params dialog if possible + if double_click and self.get_selected_block(): + self.mouse_pressed = False + Actions.BLOCK_PARAM_MODIFY() + + def handle_mouse_selector_release(self, coordinate): + """ + The selector mouse button was released: + Update the state, handle motion (dragging). + And update the selected flowgraph elements. + """ + self.set_coordinate(coordinate) + self.time = 0 + self.mouse_pressed = False + if self.element_moved: + Actions.BLOCK_MOVE() + self.element_moved = False + self.update_selected_elements() + + def handle_mouse_motion(self, coordinate): + """ + The mouse has moved, respond to mouse dragging. + Move a selected element to the new coordinate. + Auto-scroll the scroll bars at the boundaries. + """ + #to perform a movement, the mouse must be pressed + # (no longer checking pending events via gtk.events_pending() - always true in Windows) + if not self.mouse_pressed: return + #perform autoscrolling + width, height = self.get_size() + x, y = coordinate + h_adj = self.get_scroll_pane().get_hadjustment() + v_adj = self.get_scroll_pane().get_vadjustment() + for pos, length, adj, adj_val, adj_len in ( + (x, width, h_adj, h_adj.get_value(), h_adj.page_size), + (y, height, v_adj, v_adj.get_value(), v_adj.page_size), + ): + #scroll if we moved near the border + if pos-adj_val > adj_len-SCROLL_PROXIMITY_SENSITIVITY and adj_val+SCROLL_DISTANCE < length-adj_len: + adj.set_value(adj_val+SCROLL_DISTANCE) + adj.emit('changed') + elif pos-adj_val < SCROLL_PROXIMITY_SENSITIVITY: + adj.set_value(adj_val-SCROLL_DISTANCE) + adj.emit('changed') + #remove the connection if selected in drag event + if len(self.get_selected_elements()) == 1 and self.get_selected_element().is_connection(): + Actions.ELEMENT_DELETE() + #move the selected elements and record the new coordinate + X, Y = self.get_coordinate() + if not self.get_ctrl_mask(): self.move_selected((int(x - X), int(y - Y))) + self.set_coordinate((x, y)) + #queue draw for animation + self.queue_draw() diff --git a/grc/gui/MainWindow.py b/grc/gui/MainWindow.py index c2d661c668..677f202e1f 100644 --- a/grc/gui/MainWindow.py +++ b/grc/gui/MainWindow.py @@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA """ from Constants import \ - NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH + NEW_FLOGRAPH_TITLE, DEFAULT_REPORTS_WINDOW_WIDTH import Actions import pygtk pygtk.require('2.0') @@ -63,293 +63,293 @@ PAGE_TITLE_MARKUP_TMPL = """\ ############################################################ class MainWindow(gtk.Window): - """The topmost window with menus, the tool bar, and other major windows.""" + """The topmost window with menus, the tool bar, and other major windows.""" - def __init__(self, platform): - """ - MainWindow contructor - Setup the menu, toolbar, flowgraph editor notebook, block selection window... - """ - self._platform = platform - #setup window - gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) - vbox = gtk.VBox() - self.hpaned = gtk.HPaned() - self.add(vbox) - #create the menu bar and toolbar - self.add_accel_group(Actions.get_accel_group()) - vbox.pack_start(Bars.MenuBar(), False) - vbox.pack_start(Bars.Toolbar(), False) - vbox.pack_start(self.hpaned) - #create the notebook - self.notebook = gtk.Notebook() - self.page_to_be_closed = None - self.current_page = None - self.notebook.set_show_border(False) - self.notebook.set_scrollable(True) #scroll arrows for page tabs - self.notebook.connect('switch-page', self._handle_page_change) - #setup containers - self.flow_graph_vpaned = gtk.VPaned() - #flow_graph_box.pack_start(self.scrolled_window) - self.flow_graph_vpaned.pack1(self.notebook) - self.hpaned.pack1(self.flow_graph_vpaned) - self.btwin = BlockTreeWindow(platform, self.get_flow_graph); - self.hpaned.pack2(self.btwin, False) #dont allow resize - #create the reports window - self.text_display = TextDisplay() - #house the reports in a scrolled window - self.reports_scrolled_window = gtk.ScrolledWindow() - self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.reports_scrolled_window.add_with_viewport(self.text_display) - self.reports_scrolled_window.set_size_request(-1, DEFAULT_REPORTS_WINDOW_WIDTH) - self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) #dont allow resize - #load preferences and show the main window - Preferences.load(platform) - self.resize(*Preferences.main_window_size()) - self.flow_graph_vpaned.set_position(Preferences.reports_window_position()) - self.hpaned.set_position(Preferences.blocks_window_position()) - self.show_all() + def __init__(self, platform): + """ + MainWindow contructor + Setup the menu, toolbar, flowgraph editor notebook, block selection window... + """ + self._platform = platform + #setup window + gtk.Window.__init__(self, gtk.WINDOW_TOPLEVEL) + vbox = gtk.VBox() + self.hpaned = gtk.HPaned() + self.add(vbox) + #create the menu bar and toolbar + self.add_accel_group(Actions.get_accel_group()) + vbox.pack_start(Bars.MenuBar(), False) + vbox.pack_start(Bars.Toolbar(), False) + vbox.pack_start(self.hpaned) + #create the notebook + self.notebook = gtk.Notebook() + self.page_to_be_closed = None + self.current_page = None + self.notebook.set_show_border(False) + self.notebook.set_scrollable(True) #scroll arrows for page tabs + self.notebook.connect('switch-page', self._handle_page_change) + #setup containers + self.flow_graph_vpaned = gtk.VPaned() + #flow_graph_box.pack_start(self.scrolled_window) + self.flow_graph_vpaned.pack1(self.notebook) + self.hpaned.pack1(self.flow_graph_vpaned) + self.btwin = BlockTreeWindow(platform, self.get_flow_graph); + self.hpaned.pack2(self.btwin, False) #dont allow resize + #create the reports window + self.text_display = TextDisplay() + #house the reports in a scrolled window + self.reports_scrolled_window = gtk.ScrolledWindow() + self.reports_scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.reports_scrolled_window.add_with_viewport(self.text_display) + self.reports_scrolled_window.set_size_request(-1, DEFAULT_REPORTS_WINDOW_WIDTH) + self.flow_graph_vpaned.pack2(self.reports_scrolled_window, False) #dont allow resize + #load preferences and show the main window + Preferences.load(platform) + self.resize(*Preferences.main_window_size()) + self.flow_graph_vpaned.set_position(Preferences.reports_window_position()) + self.hpaned.set_position(Preferences.blocks_window_position()) + self.show_all() - ############################################################ - # Event Handlers - ############################################################ + ############################################################ + # Event Handlers + ############################################################ - def _quit(self, window, event): - """ - Handle the delete event from the main window. - Generated by pressing X to close, alt+f4, or right click+close. - This method in turns calls the state handler to quit. - - Returns: - true - """ - Actions.APPLICATION_QUIT() - return True + def _quit(self, window, event): + """ + Handle the delete event from the main window. + Generated by pressing X to close, alt+f4, or right click+close. + This method in turns calls the state handler to quit. + + Returns: + true + """ + Actions.APPLICATION_QUIT() + return True - def _handle_page_change(self, notebook, page, page_num): - """ - Handle a page change. When the user clicks on a new tab, - reload the flow graph to update the vars window and - call handle states (select nothing) to update the buttons. - - Args: - notebook: the notebook - page: new page - page_num: new page number - """ - self.current_page = self.notebook.get_nth_page(page_num) - Messages.send_page_switch(self.current_page.get_file_path()) - Actions.PAGE_CHANGE() + def _handle_page_change(self, notebook, page, page_num): + """ + Handle a page change. When the user clicks on a new tab, + reload the flow graph to update the vars window and + call handle states (select nothing) to update the buttons. + + Args: + notebook: the notebook + page: new page + page_num: new page number + """ + self.current_page = self.notebook.get_nth_page(page_num) + Messages.send_page_switch(self.current_page.get_file_path()) + Actions.PAGE_CHANGE() - ############################################################ - # Report Window - ############################################################ + ############################################################ + # Report Window + ############################################################ - def add_report_line(self, line): - """ - Place line at the end of the text buffer, then scroll its window all the way down. - - Args: - line: the new text - """ - self.text_display.insert(line) - vadj = self.reports_scrolled_window.get_vadjustment() - vadj.set_value(vadj.upper) - vadj.emit('changed') + def add_report_line(self, line): + """ + Place line at the end of the text buffer, then scroll its window all the way down. + + Args: + line: the new text + """ + self.text_display.insert(line) + vadj = self.reports_scrolled_window.get_vadjustment() + vadj.set_value(vadj.upper) + vadj.emit('changed') - ############################################################ - # Pages: create and close - ############################################################ + ############################################################ + # Pages: create and close + ############################################################ - def new_page(self, file_path='', show=False): - """ - Create a new notebook page. - Set the tab to be selected. - - Args: - file_path: optional file to load into the flow graph - show: true if the page should be shown after loading - """ - #if the file is already open, show the open page and return - if file_path and file_path in self._get_files(): #already open - page = self.notebook.get_nth_page(self._get_files().index(file_path)) - self._set_page(page) - return - try: #try to load from file - if file_path: Messages.send_start_load(file_path) - flow_graph = self._platform.get_new_flow_graph() - flow_graph.grc_file_path = file_path; - #print flow_graph - page = NotebookPage( - self, - flow_graph=flow_graph, - file_path=file_path, - ) - if file_path: Messages.send_end_load() - except Exception, e: #return on failure - Messages.send_fail_load(e) - if isinstance(e, KeyError) and str(e) == "'options'": - # This error is unrecoverable, so crash gracefully - exit(-1) - return - #add this page to the notebook - self.notebook.append_page(page, page.get_tab()) - try: self.notebook.set_tab_reorderable(page, True) - except: pass #gtk too old - self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START) - #only show if blank or manual - if not file_path or show: self._set_page(page) + def new_page(self, file_path='', show=False): + """ + Create a new notebook page. + Set the tab to be selected. + + Args: + file_path: optional file to load into the flow graph + show: true if the page should be shown after loading + """ + #if the file is already open, show the open page and return + if file_path and file_path in self._get_files(): #already open + page = self.notebook.get_nth_page(self._get_files().index(file_path)) + self._set_page(page) + return + try: #try to load from file + if file_path: Messages.send_start_load(file_path) + flow_graph = self._platform.get_new_flow_graph() + flow_graph.grc_file_path = file_path; + #print flow_graph + page = NotebookPage( + self, + flow_graph=flow_graph, + file_path=file_path, + ) + if file_path: Messages.send_end_load() + except Exception, e: #return on failure + Messages.send_fail_load(e) + if isinstance(e, KeyError) and str(e) == "'options'": + # This error is unrecoverable, so crash gracefully + exit(-1) + return + #add this page to the notebook + self.notebook.append_page(page, page.get_tab()) + try: self.notebook.set_tab_reorderable(page, True) + except: pass #gtk too old + self.notebook.set_tab_label_packing(page, False, False, gtk.PACK_START) + #only show if blank or manual + if not file_path or show: self._set_page(page) - def close_pages(self): - """ - Close all the pages in this notebook. - - Returns: - true if all closed - """ - open_files = filter(lambda file: file, self._get_files()) #filter blank files - open_file = self.get_page().get_file_path() - #close each page - for page in self._get_pages(): - self.page_to_be_closed = page - self.close_page(False) - if self.notebook.get_n_pages(): return False - #save state before closing - Preferences.files_open(open_files) - Preferences.file_open(open_file) - Preferences.main_window_size(self.get_size()) - Preferences.reports_window_position(self.flow_graph_vpaned.get_position()) - Preferences.blocks_window_position(self.hpaned.get_position()) - Preferences.save() - return True + def close_pages(self): + """ + Close all the pages in this notebook. + + Returns: + true if all closed + """ + open_files = filter(lambda file: file, self._get_files()) #filter blank files + open_file = self.get_page().get_file_path() + #close each page + for page in self._get_pages(): + self.page_to_be_closed = page + self.close_page(False) + if self.notebook.get_n_pages(): return False + #save state before closing + Preferences.files_open(open_files) + Preferences.file_open(open_file) + Preferences.main_window_size(self.get_size()) + Preferences.reports_window_position(self.flow_graph_vpaned.get_position()) + Preferences.blocks_window_position(self.hpaned.get_position()) + Preferences.save() + return True - def close_page(self, ensure=True): - """ - Close the current page. - If the notebook becomes empty, and ensure is true, - call new page upon exit to ensure that at least one page exists. - - Args: - ensure: boolean - """ - if not self.page_to_be_closed: self.page_to_be_closed = self.get_page() - #show the page if it has an executing flow graph or is unsaved - if self.page_to_be_closed.get_proc() or not self.page_to_be_closed.get_saved(): - self._set_page(self.page_to_be_closed) - #unsaved? ask the user - if not self.page_to_be_closed.get_saved() and self._save_changes(): - Actions.FLOW_GRAPH_SAVE() #try to save - if not self.page_to_be_closed.get_saved(): #still unsaved? - self.page_to_be_closed = None #set the page to be closed back to None - return - #stop the flow graph if executing - if self.page_to_be_closed.get_proc(): Actions.FLOW_GRAPH_KILL() - #remove the page - self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed)) - if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one - self.page_to_be_closed = None #set the page to be closed back to None + def close_page(self, ensure=True): + """ + Close the current page. + If the notebook becomes empty, and ensure is true, + call new page upon exit to ensure that at least one page exists. + + Args: + ensure: boolean + """ + if not self.page_to_be_closed: self.page_to_be_closed = self.get_page() + #show the page if it has an executing flow graph or is unsaved + if self.page_to_be_closed.get_proc() or not self.page_to_be_closed.get_saved(): + self._set_page(self.page_to_be_closed) + #unsaved? ask the user + if not self.page_to_be_closed.get_saved() and self._save_changes(): + Actions.FLOW_GRAPH_SAVE() #try to save + if not self.page_to_be_closed.get_saved(): #still unsaved? + self.page_to_be_closed = None #set the page to be closed back to None + return + #stop the flow graph if executing + if self.page_to_be_closed.get_proc(): Actions.FLOW_GRAPH_KILL() + #remove the page + self.notebook.remove_page(self.notebook.page_num(self.page_to_be_closed)) + if ensure and self.notebook.get_n_pages() == 0: self.new_page() #no pages, make a new one + self.page_to_be_closed = None #set the page to be closed back to None - ############################################################ - # Misc - ############################################################ + ############################################################ + # Misc + ############################################################ - def update(self): - """ - Set the title of the main window. - Set the titles on the page tabs. - Show/hide the reports window. - - Args: - title: the window title - """ - gtk.Window.set_title(self, Utils.parse_template(MAIN_WINDOW_TITLE_TMPL, - basename=os.path.basename(self.get_page().get_file_path()), - dirname=os.path.dirname(self.get_page().get_file_path()), - new_flowgraph_title=NEW_FLOGRAPH_TITLE, - read_only=self.get_page().get_read_only(), - saved=self.get_page().get_saved(), - platform_name=self._platform.get_name(), - ) - ) - #set tab titles - for page in self._get_pages(): page.set_markup( - Utils.parse_template(PAGE_TITLE_MARKUP_TMPL, - #get filename and strip out file extension - title=os.path.splitext(os.path.basename(page.get_file_path()))[0], - read_only=page.get_read_only(), saved=page.get_saved(), - new_flowgraph_title=NEW_FLOGRAPH_TITLE, - ) - ) - #show/hide notebook tabs - self.notebook.set_show_tabs(len(self._get_pages()) > 1) + def update(self): + """ + Set the title of the main window. + Set the titles on the page tabs. + Show/hide the reports window. + + Args: + title: the window title + """ + gtk.Window.set_title(self, Utils.parse_template(MAIN_WINDOW_TITLE_TMPL, + basename=os.path.basename(self.get_page().get_file_path()), + dirname=os.path.dirname(self.get_page().get_file_path()), + new_flowgraph_title=NEW_FLOGRAPH_TITLE, + read_only=self.get_page().get_read_only(), + saved=self.get_page().get_saved(), + platform_name=self._platform.get_name(), + ) + ) + #set tab titles + for page in self._get_pages(): page.set_markup( + Utils.parse_template(PAGE_TITLE_MARKUP_TMPL, + #get filename and strip out file extension + title=os.path.splitext(os.path.basename(page.get_file_path()))[0], + read_only=page.get_read_only(), saved=page.get_saved(), + new_flowgraph_title=NEW_FLOGRAPH_TITLE, + ) + ) + #show/hide notebook tabs + self.notebook.set_show_tabs(len(self._get_pages()) > 1) - def get_page(self): - """ - Get the selected page. - - Returns: - the selected page - """ - return self.current_page + def get_page(self): + """ + Get the selected page. + + Returns: + the selected page + """ + return self.current_page - def get_flow_graph(self): - """ - Get the selected flow graph. - - Returns: - the selected flow graph - """ - return self.get_page().get_flow_graph() + def get_flow_graph(self): + """ + Get the selected flow graph. + + Returns: + the selected flow graph + """ + return self.get_page().get_flow_graph() - def get_focus_flag(self): - """ - Get the focus flag from the current page. - - Returns: - the focus flag - """ - return self.get_page().get_drawing_area().get_focus_flag() + def get_focus_flag(self): + """ + Get the focus flag from the current page. + + Returns: + the focus flag + """ + return self.get_page().get_drawing_area().get_focus_flag() - ############################################################ - # Helpers - ############################################################ + ############################################################ + # Helpers + ############################################################ - def _set_page(self, page): - """ - Set the current page. - - Args: - page: the page widget - """ - self.current_page = page - self.notebook.set_current_page(self.notebook.page_num(self.current_page)) + def _set_page(self, page): + """ + Set the current page. + + Args: + page: the page widget + """ + self.current_page = page + self.notebook.set_current_page(self.notebook.page_num(self.current_page)) - def _save_changes(self): - """ - Save changes to flow graph? - - Returns: - true if yes - """ - return MessageDialogHelper( - gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!', - 'Would you like to save changes before closing?' - ) == gtk.RESPONSE_YES + def _save_changes(self): + """ + Save changes to flow graph? + + Returns: + true if yes + """ + return MessageDialogHelper( + gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, 'Unsaved Changes!', + 'Would you like to save changes before closing?' + ) == gtk.RESPONSE_YES - def _get_files(self): - """ - Get the file names for all the pages, in order. - - Returns: - list of file paths - """ - return map(lambda page: page.get_file_path(), self._get_pages()) + def _get_files(self): + """ + Get the file names for all the pages, in order. + + Returns: + list of file paths + """ + return map(lambda page: page.get_file_path(), self._get_pages()) - def _get_pages(self): - """ - Get a list of all pages in the notebook. - - Returns: - list of pages - """ - return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())] + def _get_pages(self): + """ + Get a list of all pages in the notebook. + + Returns: + list of pages + """ + return [self.notebook.get_nth_page(page_num) for page_num in range(self.notebook.get_n_pages())] diff --git a/grc/gui/Messages.py b/grc/gui/Messages.py index a9f0e36b85..d903e40a45 100644 --- a/grc/gui/Messages.py +++ b/grc/gui/Messages.py @@ -24,22 +24,22 @@ import sys MESSENGERS_LIST = list() def register_messenger(messenger): - """ - Append the given messenger to the list of messengers. - - Args: - messenger: a method thats takes a string - """ - MESSENGERS_LIST.append(messenger) + """ + Append the given messenger to the list of messengers. + + Args: + messenger: a method thats takes a string + """ + MESSENGERS_LIST.append(messenger) def send(message): - """ - Give the message to each of the messengers. - - Args: - message: a message string - """ - for messenger in MESSENGERS_LIST: messenger(message) + """ + Give the message to each of the messengers. + + Args: + message: a message string + """ + for messenger in MESSENGERS_LIST: messenger(message) #register stdout by default register_messenger(sys.stdout.write) @@ -48,61 +48,61 @@ register_messenger(sys.stdout.write) # Special functions for specific program functionalities ########################################################################### def send_init(platform): - send("""<<< Welcome to %s %s >>>\n"""%(platform.get_name(), platform.get_version())) + send("""<<< Welcome to %s %s >>>\n"""%(platform.get_name(), platform.get_version())) def send_page_switch(file_path): - send('\nShowing: "%s"\n'%file_path) + send('\nShowing: "%s"\n'%file_path) -################# functions for loading flow graphs ######################################## +################# functions for loading flow graphs ######################################## def send_start_load(file_path): - send('\nLoading: "%s"'%file_path + '\n') + send('\nLoading: "%s"'%file_path + '\n') def send_error_load(error): - send('>>> Error: %s\n'%error) - traceback.print_exc() + send('>>> Error: %s\n'%error) + traceback.print_exc() def send_end_load(): - send('>>> Done\n') + send('>>> Done\n') def send_fail_load(error): - send('Error: %s\n'%error) - send('>>> Failure\n') - traceback.print_exc() + send('Error: %s\n'%error) + send('>>> Failure\n') + traceback.print_exc() -################# functions for generating flow graphs ######################################## +################# functions for generating flow graphs ######################################## def send_start_gen(file_path): - send('\nGenerating: "%s"'%file_path + '\n') + send('\nGenerating: "%s"'%file_path + '\n') def send_fail_gen(error): - send('Generate Error: %s\n'%error) - send('>>> Failure\n') - traceback.print_exc() + send('Generate Error: %s\n'%error) + send('>>> Failure\n') + traceback.print_exc() -################# functions for executing flow graphs ######################################## +################# functions for executing flow graphs ######################################## def send_start_exec(file_path): - send('\nExecuting: "%s"'%file_path + '\n') + send('\nExecuting: "%s"'%file_path + '\n') def send_verbose_exec(verbose): - send(verbose) + send(verbose) def send_end_exec(): - send('\n>>> Done\n') + send('\n>>> Done\n') -################# functions for saving flow graphs ######################################## +################# functions for saving flow graphs ######################################## def send_fail_save(file_path): - send('>>> Error: Cannot save: %s\n'%file_path) + send('>>> Error: Cannot save: %s\n'%file_path) -################# functions for connections ######################################## +################# functions for connections ######################################## def send_fail_connection(): - send('>>> Error: Cannot create connection.\n') + send('>>> Error: Cannot create connection.\n') -################# functions for preferences ######################################## +################# functions for preferences ######################################## def send_fail_load_preferences(prefs_file_path): - send('>>> Error: Cannot load preferences file: "%s"\n'%prefs_file_path) + send('>>> Error: Cannot load preferences file: "%s"\n'%prefs_file_path) def send_fail_save_preferences(prefs_file_path): - send('>>> Error: Cannot save preferences file: "%s"\n'%prefs_file_path) + send('>>> Error: Cannot save preferences file: "%s"\n'%prefs_file_path) -################# functions for warning ######################################## +################# functions for warning ######################################## def send_warning(warning): - send('>>> Warning: %s\n'%warning) + send('>>> Warning: %s\n'%warning) diff --git a/grc/gui/NotebookPage.py b/grc/gui/NotebookPage.py index 095c045a66..10c5dde00d 100644 --- a/grc/gui/NotebookPage.py +++ b/grc/gui/NotebookPage.py @@ -31,185 +31,185 @@ import os ############################################################ class NotebookPage(gtk.HBox): - """A page in the notebook.""" - - def __init__(self, main_window, flow_graph, file_path=''): - """ - Page constructor. - - Args: - main_window: main window - file_path: path to a flow graph file - """ - self._flow_graph = flow_graph - self.set_proc(None) - #import the file - self.main_window = main_window - self.set_file_path(file_path) - initial_state = flow_graph.get_parent().parse_flow_graph(file_path) - self.state_cache = StateCache(initial_state) - self.set_saved(True) - #import the data to the flow graph - self.get_flow_graph().import_data(initial_state) - #initialize page gui - gtk.HBox.__init__(self, False, 0) - self.show() - #tab box to hold label and close button - self.tab = gtk.HBox(False, 0) - #setup tab label - self.label = gtk.Label() - self.tab.pack_start(self.label, False) - #setup button image - image = gtk.Image() - image.set_from_stock('gtk-close', gtk.ICON_SIZE_MENU) - #setup image box - image_box = gtk.HBox(False, 0) - image_box.pack_start(image, True, False, 0) - #setup the button - button = gtk.Button() - button.connect("clicked", self._handle_button) - button.set_relief(gtk.RELIEF_NONE) - button.add(image_box) - #button size - w, h = gtk.icon_size_lookup_for_settings(button.get_settings(), gtk.ICON_SIZE_MENU) - button.set_size_request(w+6, h+6) - self.tab.pack_start(button, False) - self.tab.show_all() - #setup scroll window and drawing area - self.scrolled_window = gtk.ScrolledWindow() - self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) - self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - self.drawing_area = DrawingArea(self.get_flow_graph()) - self.scrolled_window.add_with_viewport(self.get_drawing_area()) - self.pack_start(self.scrolled_window) - #inject drawing area into flow graph - self.get_flow_graph().drawing_area = self.get_drawing_area() - self.show_all() - - def get_drawing_area(self): return self.drawing_area - - def get_generator(self): - """ - Get the generator object for this flow graph. - - Returns: - generator - """ - return self.get_flow_graph().get_parent().get_generator()( - self.get_flow_graph(), - self.get_file_path(), - ) - - def _handle_button(self, button): - """ - The button was clicked. - Make the current page selected, then close. - - Args: - the: button - """ - self.main_window.page_to_be_closed = self - Actions.FLOW_GRAPH_CLOSE() - - def set_markup(self, markup): - """ - Set the markup in this label. - - Args: - markup: the new markup text - """ - self.label.set_markup(markup) - - def get_tab(self): - """ - Get the gtk widget for this page's tab. - - Returns: - gtk widget - """ - return self.tab - - def get_proc(self): - """ - Get the subprocess for the flow graph. - - Returns: - the subprocess object - """ - return self.process - - def set_proc(self, process): - """ - Set the subprocess object. - - Args: - process: the new subprocess - """ - self.process = process - - def get_flow_graph(self): - """ - Get the flow graph. - - Returns: - the flow graph - """ - return self._flow_graph - - def get_read_only(self): - """ - Get the read-only state of the file. - Always false for empty path. - - Returns: - true for read-only - """ - if not self.get_file_path(): return False - return os.path.exists(self.get_file_path()) and \ - not os.access(self.get_file_path(), os.W_OK) - - def get_file_path(self): - """ - Get the file path for the flow graph. - - Returns: - the file path or '' - """ - return self.file_path - - def set_file_path(self, file_path=''): - """ - Set the file path, '' for no file path. - - Args: - file_path: file path string - """ - if file_path: self.file_path = os.path.abspath(file_path) - else: self.file_path = '' - - def get_saved(self): - """ - Get the saved status for the flow graph. - - Returns: - true if saved - """ - return self.saved - - def set_saved(self, saved=True): - """ - Set the saved status. - - Args: - saved: boolean status - """ - self.saved = saved - - def get_state_cache(self): - """ - Get the state cache for the flow graph. - - Returns: - the state cache - """ - return self.state_cache + """A page in the notebook.""" + + def __init__(self, main_window, flow_graph, file_path=''): + """ + Page constructor. + + Args: + main_window: main window + file_path: path to a flow graph file + """ + self._flow_graph = flow_graph + self.set_proc(None) + #import the file + self.main_window = main_window + self.set_file_path(file_path) + initial_state = flow_graph.get_parent().parse_flow_graph(file_path) + self.state_cache = StateCache(initial_state) + self.set_saved(True) + #import the data to the flow graph + self.get_flow_graph().import_data(initial_state) + #initialize page gui + gtk.HBox.__init__(self, False, 0) + self.show() + #tab box to hold label and close button + self.tab = gtk.HBox(False, 0) + #setup tab label + self.label = gtk.Label() + self.tab.pack_start(self.label, False) + #setup button image + image = gtk.Image() + image.set_from_stock('gtk-close', gtk.ICON_SIZE_MENU) + #setup image box + image_box = gtk.HBox(False, 0) + image_box.pack_start(image, True, False, 0) + #setup the button + button = gtk.Button() + button.connect("clicked", self._handle_button) + button.set_relief(gtk.RELIEF_NONE) + button.add(image_box) + #button size + w, h = gtk.icon_size_lookup_for_settings(button.get_settings(), gtk.ICON_SIZE_MENU) + button.set_size_request(w+6, h+6) + self.tab.pack_start(button, False) + self.tab.show_all() + #setup scroll window and drawing area + self.scrolled_window = gtk.ScrolledWindow() + self.scrolled_window.set_size_request(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT) + self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self.drawing_area = DrawingArea(self.get_flow_graph()) + self.scrolled_window.add_with_viewport(self.get_drawing_area()) + self.pack_start(self.scrolled_window) + #inject drawing area into flow graph + self.get_flow_graph().drawing_area = self.get_drawing_area() + self.show_all() + + def get_drawing_area(self): return self.drawing_area + + def get_generator(self): + """ + Get the generator object for this flow graph. + + Returns: + generator + """ + return self.get_flow_graph().get_parent().get_generator()( + self.get_flow_graph(), + self.get_file_path(), + ) + + def _handle_button(self, button): + """ + The button was clicked. + Make the current page selected, then close. + + Args: + the: button + """ + self.main_window.page_to_be_closed = self + Actions.FLOW_GRAPH_CLOSE() + + def set_markup(self, markup): + """ + Set the markup in this label. + + Args: + markup: the new markup text + """ + self.label.set_markup(markup) + + def get_tab(self): + """ + Get the gtk widget for this page's tab. + + Returns: + gtk widget + """ + return self.tab + + def get_proc(self): + """ + Get the subprocess for the flow graph. + + Returns: + the subprocess object + """ + return self.process + + def set_proc(self, process): + """ + Set the subprocess object. + + Args: + process: the new subprocess + """ + self.process = process + + def get_flow_graph(self): + """ + Get the flow graph. + + Returns: + the flow graph + """ + return self._flow_graph + + def get_read_only(self): + """ + Get the read-only state of the file. + Always false for empty path. + + Returns: + true for read-only + """ + if not self.get_file_path(): return False + return os.path.exists(self.get_file_path()) and \ + not os.access(self.get_file_path(), os.W_OK) + + def get_file_path(self): + """ + Get the file path for the flow graph. + + Returns: + the file path or '' + """ + return self.file_path + + def set_file_path(self, file_path=''): + """ + Set the file path, '' for no file path. + + Args: + file_path: file path string + """ + if file_path: self.file_path = os.path.abspath(file_path) + else: self.file_path = '' + + def get_saved(self): + """ + Get the saved status for the flow graph. + + Returns: + true if saved + """ + return self.saved + + def set_saved(self, saved=True): + """ + Set the saved status. + + Args: + saved: boolean status + """ + self.saved = saved + + def get_state_cache(self): + """ + Get the state cache for the flow graph. + + Returns: + the state cache + """ + return self.state_cache diff --git a/grc/gui/Param.py b/grc/gui/Param.py index da76b6b82c..f0e5a2fcb2 100644 --- a/grc/gui/Param.py +++ b/grc/gui/Param.py @@ -25,110 +25,110 @@ import gtk import Colors class InputParam(gtk.HBox): - """The base class for an input parameter inside the input parameters dialog.""" - - def __init__(self, param, callback=None): - gtk.HBox.__init__(self) - self.param = param - self._callback = callback - self.label = gtk.Label() #no label, markup is added by set_markup - self.label.set_size_request(150, -1) - self.pack_start(self.label, False) - self.set_markup = lambda m: self.label.set_markup(m) - self.tp = None - #connect events - self.connect('show', self._update_gui) - def set_color(self, color): pass - def set_tooltip_text(self, text): pass - - def _update_gui(self, *args): - """ - Set the markup, color, tooltip, show/hide. - """ - #set the markup - has_cb = \ - hasattr(self.param.get_parent(), 'get_callbacks') and \ - filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks) - self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb)) - #set the color - self.set_color(self.param.get_color()) - #set the tooltip - self.set_tooltip_text( - Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(), - ) - #show/hide - if self.param.get_hide() == 'all': self.hide_all() - else: self.show_all() - - def _handle_changed(self, *args): - """ - Handle a gui change by setting the new param value, - calling the callback (if applicable), and updating. - """ - #set the new value - self.param.set_value(self.get_text()) - #call the callback - if self._callback: self._callback(*args) - else: self.param.validate() - #gui update - self._update_gui() + """The base class for an input parameter inside the input parameters dialog.""" + + def __init__(self, param, callback=None): + gtk.HBox.__init__(self) + self.param = param + self._callback = callback + self.label = gtk.Label() #no label, markup is added by set_markup + self.label.set_size_request(150, -1) + self.pack_start(self.label, False) + self.set_markup = lambda m: self.label.set_markup(m) + self.tp = None + #connect events + self.connect('show', self._update_gui) + def set_color(self, color): pass + def set_tooltip_text(self, text): pass + + def _update_gui(self, *args): + """ + Set the markup, color, tooltip, show/hide. + """ + #set the markup + has_cb = \ + hasattr(self.param.get_parent(), 'get_callbacks') and \ + filter(lambda c: self.param.get_key() in c, self.param.get_parent()._callbacks) + self.set_markup(Utils.parse_template(PARAM_LABEL_MARKUP_TMPL, param=self.param, has_cb=has_cb)) + #set the color + self.set_color(self.param.get_color()) + #set the tooltip + self.set_tooltip_text( + Utils.parse_template(TIP_MARKUP_TMPL, param=self.param).strip(), + ) + #show/hide + if self.param.get_hide() == 'all': self.hide_all() + else: self.show_all() + + def _handle_changed(self, *args): + """ + Handle a gui change by setting the new param value, + calling the callback (if applicable), and updating. + """ + #set the new value + self.param.set_value(self.get_text()) + #call the callback + if self._callback: self._callback(*args) + else: self.param.validate() + #gui update + self._update_gui() class EntryParam(InputParam): - """Provide an entry box for strings and numbers.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.Entry() - self._input.set_text(self.param.get_value()) - self._input.connect('changed', self._handle_changed) - self.pack_start(self._input, True) - def get_text(self): return self._input.get_text() - def set_color(self, color): - self._input.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - self._input.modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) - def set_tooltip_text(self, text): self._input.set_tooltip_text(text) + """Provide an entry box for strings and numbers.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.Entry() + self._input.set_text(self.param.get_value()) + self._input.connect('changed', self._handle_changed) + self.pack_start(self._input, True) + def get_text(self): return self._input.get_text() + def set_color(self, color): + self._input.modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) + self._input.modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + def set_tooltip_text(self, text): self._input.set_tooltip_text(text) class EnumParam(InputParam): - """Provide an entry box for Enum types with a drop down menu.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - self._input.connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): return self.param.get_option_keys()[self._input.get_active()] - def set_tooltip_text(self, text): self._input.set_tooltip_text(text) + """Provide an entry box for Enum types with a drop down menu.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + self._input.connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): return self.param.get_option_keys()[self._input.get_active()] + def set_tooltip_text(self, text): self._input.set_tooltip_text(text) class EnumEntryParam(InputParam): - """Provide an entry box and drop down menu for Raw Enum types.""" - - def __init__(self, *args, **kwargs): - InputParam.__init__(self, *args, **kwargs) - self._input = gtk.combo_box_entry_new_text() - for option in self.param.get_options(): self._input.append_text(option.get_name()) - try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) - except: - self._input.set_active(-1) - self._input.get_child().set_text(self.param.get_value()) - self._input.connect('changed', self._handle_changed) - self._input.get_child().connect('changed', self._handle_changed) - self.pack_start(self._input, False) - def get_text(self): - if self._input.get_active() == -1: return self._input.get_child().get_text() - return self.param.get_option_keys()[self._input.get_active()] - def set_tooltip_text(self, text): - if self._input.get_active() == -1: #custom entry - self._input.get_child().set_tooltip_text(text) - else: self._input.set_tooltip_text(text) - def set_color(self, color): - if self._input.get_active() == -1: #custom entry, use color - self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) - self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) - else: #from enum, make pale background - self._input.get_child().modify_base(gtk.STATE_NORMAL, Colors.ENTRYENUM_CUSTOM_COLOR) - self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + """Provide an entry box and drop down menu for Raw Enum types.""" + + def __init__(self, *args, **kwargs): + InputParam.__init__(self, *args, **kwargs) + self._input = gtk.combo_box_entry_new_text() + for option in self.param.get_options(): self._input.append_text(option.get_name()) + try: self._input.set_active(self.param.get_option_keys().index(self.param.get_value())) + except: + self._input.set_active(-1) + self._input.get_child().set_text(self.param.get_value()) + self._input.connect('changed', self._handle_changed) + self._input.get_child().connect('changed', self._handle_changed) + self.pack_start(self._input, False) + def get_text(self): + if self._input.get_active() == -1: return self._input.get_child().get_text() + return self.param.get_option_keys()[self._input.get_active()] + def set_tooltip_text(self, text): + if self._input.get_active() == -1: #custom entry + self._input.get_child().set_tooltip_text(text) + else: self._input.set_tooltip_text(text) + def set_color(self, color): + if self._input.get_active() == -1: #custom entry, use color + self._input.get_child().modify_base(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) + self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) + else: #from enum, make pale background + self._input.get_child().modify_base(gtk.STATE_NORMAL, Colors.ENTRYENUM_CUSTOM_COLOR) + self._input.get_child().modify_text(gtk.STATE_NORMAL, Colors.PARAM_ENTRY_TEXT_COLOR) PARAM_MARKUP_TMPL="""\ #set $foreground = $param.is_valid() and 'black' or 'red' @@ -142,13 +142,13 @@ PARAM_LABEL_MARKUP_TMPL="""\ TIP_MARKUP_TMPL="""\ ######################################## #def truncate(string) - #set $max_len = 100 - #set $string = str($string) - #if len($string) > $max_len + #set $max_len = 100 + #set $string = str($string) + #if len($string) > $max_len $('%s...%s'%($string[:$max_len/2], $string[-$max_len/2:]))#slurp - #else + #else $string#slurp - #end if + #end if #end def ######################################## Key: $param.get_key() @@ -159,35 +159,35 @@ Value: $truncate($param.get_evaluated()) Error: $(param.get_error_messages()[0]) #else Error: - #for $error_msg in $param.get_error_messages() + #for $error_msg in $param.get_error_messages() * $error_msg - #end for + #end for #end if""" class Param(Element): - """The graphical parameter.""" - - def __init__(self): Element.__init__(self) - - def get_input(self, *args, **kwargs): - """ - Get the graphical gtk class to represent this parameter. - An enum requires and combo parameter. - A non-enum with options gets a combined entry/combo parameter. - All others get a standard entry parameter. - - Returns: - gtk input class - """ - if self.is_enum(): return EnumParam(self, *args, **kwargs) - if self.get_options(): return EnumEntryParam(self, *args, **kwargs) - return EntryParam(self, *args, **kwargs) - - def get_markup(self): - """ - Get the markup for this param. - - Returns: - a pango markup string - """ - return Utils.parse_template(PARAM_MARKUP_TMPL, param=self) + """The graphical parameter.""" + + def __init__(self): Element.__init__(self) + + def get_input(self, *args, **kwargs): + """ + Get the graphical gtk class to represent this parameter. + An enum requires and combo parameter. + A non-enum with options gets a combined entry/combo parameter. + All others get a standard entry parameter. + + Returns: + gtk input class + """ + if self.is_enum(): return EnumParam(self, *args, **kwargs) + if self.get_options(): return EnumEntryParam(self, *args, **kwargs) + return EntryParam(self, *args, **kwargs) + + def get_markup(self): + """ + Get the markup for this param. + + Returns: + a pango markup string + """ + return Utils.parse_template(PARAM_MARKUP_TMPL, param=self) diff --git a/grc/gui/Platform.py b/grc/gui/Platform.py index 8bbfaca232..6a8175b9fa 100644 --- a/grc/gui/Platform.py +++ b/grc/gui/Platform.py @@ -20,4 +20,4 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Element import Element class Platform(Element): - def __init__(self): Element.__init__(self) + def __init__(self): Element.__init__(self) diff --git a/grc/gui/Port.py b/grc/gui/Port.py index 7b4c27dd5f..fe1dc5070a 100644 --- a/grc/gui/Port.py +++ b/grc/gui/Port.py @@ -19,9 +19,9 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA from Element import Element from Constants import \ - PORT_SEPARATION, CONNECTOR_EXTENSION_MINIMAL, \ - CONNECTOR_EXTENSION_INCREMENT, \ - PORT_LABEL_PADDING, PORT_MIN_WIDTH + PORT_SEPARATION, CONNECTOR_EXTENSION_MINIMAL, \ + CONNECTOR_EXTENSION_INCREMENT, \ + PORT_LABEL_PADDING, PORT_MIN_WIDTH import Utils import Colors import pygtk @@ -32,179 +32,192 @@ PORT_MARKUP_TMPL="""\ <span foreground="black" font_desc="Sans 7.5">$encode($port.get_name())</span>""" class Port(Element): - """The graphical port.""" - - def __init__(self): - """ - Port contructor. - Create list of connector coordinates. - """ - Element.__init__(self) - self.connector_coordinates = dict() - - def create_shapes(self): - """Create new areas and labels for the port.""" - Element.create_shapes(self) - #get current rotation - rotation = self.get_rotation() - #get all sibling ports - if self.is_source(): ports = self.get_parent().get_sources() - elif self.is_sink(): ports = self.get_parent().get_sinks() - #get the max width - self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) - #get a numeric index for this port relative to its sibling ports - index = ports.index(self) - length = len(ports) - #reverse the order of ports for these rotations - if rotation in (180, 270): index = length-index-1 - offset = (self.get_parent().H - length*self.H - (length-1)*PORT_SEPARATION)/2 - #create areas and connector coordinates - if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): - x = -1*self.W - y = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.W, self.H)) - self._connector_coordinate = (x-1, y+self.H/2) - elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): - x = self.get_parent().W - y = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.W, self.H)) - self._connector_coordinate = (x+1+self.W, y+self.H/2) - elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): - y = -1*self.W - x = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.H, self.W)) - self._connector_coordinate = (x+self.H/2, y-1) - elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): - y = self.get_parent().W - x = (PORT_SEPARATION+self.H)*index+offset - self.add_area((x, y), (self.H, self.W)) - self._connector_coordinate = (x+self.H/2, y+1+self.W) - #the connector length - self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index - - def create_labels(self): - """Create the labels for the socket.""" - Element.create_labels(self) - self._bg_color = Colors.get_color(self.get_color()) - #create the layout - layout = gtk.DrawingArea().create_pango_layout('') - layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self)) - self.w, self.h = layout.get_pixel_size() - self.W, self.H = 2*PORT_LABEL_PADDING+self.w, 2*PORT_LABEL_PADDING+self.h - #create the pixmap - pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) - gc = pixmap.new_gc() - gc.set_foreground(self._bg_color) - pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h) - pixmap.draw_layout(gc, 0, 0, layout) - #create vertical and horizontal pixmaps - self.horizontal_label = pixmap - if self.is_vertical(): - self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w) - Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) - - def draw(self, gc, window): - """ - Draw the socket with a label. - - Args: - gc: the graphics context - window: the gtk window to draw on - """ - Element.draw( - self, gc, window, bg_color=self._bg_color, - border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, - ) - X,Y = self.get_coordinate() - (x,y),(w,h) = self._areas_list[0] #use the first area's sizes to place the labels - if self.is_horizontal(): - window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1) - elif self.is_vertical(): - window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1) - - def get_connector_coordinate(self): - """ - Get the coordinate where connections may attach to. - - Returns: - the connector coordinate (x, y) tuple - """ - x,y = self._connector_coordinate - X,Y = self.get_coordinate() - return (x+X, y+Y) - - def get_connector_direction(self): - """ - Get the direction that the socket points: 0,90,180,270. - This is the rotation degree if the socket is an output or - the rotation degree + 180 if the socket is an input. - - Returns: - the direction in degrees - """ - if self.is_source(): return self.get_rotation() - elif self.is_sink(): return (self.get_rotation() + 180)%360 - - def get_connector_length(self): - """ - Get the length of the connector. - The connector length increases as the port index changes. - - Returns: - the length in pixels - """ - return self._connector_length - - def get_rotation(self): - """ - Get the parent's rotation rather than self. - - Returns: - the parent's rotation - """ - return self.get_parent().get_rotation() - - def move(self, delta_coor): - """ - Move the parent rather than self. - - Args: - delta_corr: the (delta_x, delta_y) tuple - """ - self.get_parent().move(delta_coor) - - def rotate(self, direction): - """ - Rotate the parent rather than self. - - Args: - direction: degrees to rotate - """ - self.get_parent().rotate(direction) - - def get_coordinate(self): - """ - Get the parent's coordinate rather than self. - - Returns: - the parents coordinate - """ - return self.get_parent().get_coordinate() - - def set_highlighted(self, highlight): - """ - Set the parent highlight rather than self. - - Args: - highlight: true to enable highlighting - """ - self.get_parent().set_highlighted(highlight) - - def is_highlighted(self): - """ - Get the parent's is highlight rather than self. - - Returns: - the parent's highlighting status - """ - return self.get_parent().is_highlighted() + """The graphical port.""" + + def __init__(self): + """ + Port contructor. + Create list of connector coordinates. + """ + Element.__init__(self) + self.connector_coordinates = dict() + + def create_shapes(self): + """Create new areas and labels for the port.""" + Element.create_shapes(self) + #get current rotation + rotation = self.get_rotation() + #get all sibling ports + if self.is_source(): ports = self.get_parent().get_sources_gui() + elif self.is_sink(): ports = self.get_parent().get_sinks_gui() + #get the max width + self.W = max([port.W for port in ports] + [PORT_MIN_WIDTH]) + #get a numeric index for this port relative to its sibling ports + try: + index = ports.index(self) + except: + if hasattr(self, '_connector_length'): + del self._connector_length; + return + length = len(ports) + #reverse the order of ports for these rotations + if rotation in (180, 270): index = length-index-1 + offset = (self.get_parent().H - length*self.H - (length-1)*PORT_SEPARATION)/2 + #create areas and connector coordinates + if (self.is_sink() and rotation == 0) or (self.is_source() and rotation == 180): + x = -1*self.W + y = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.W, self.H)) + self._connector_coordinate = (x-1, y+self.H/2) + elif (self.is_source() and rotation == 0) or (self.is_sink() and rotation == 180): + x = self.get_parent().W + y = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.W, self.H)) + self._connector_coordinate = (x+1+self.W, y+self.H/2) + elif (self.is_source() and rotation == 90) or (self.is_sink() and rotation == 270): + y = -1*self.W + x = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.H, self.W)) + self._connector_coordinate = (x+self.H/2, y-1) + elif (self.is_sink() and rotation == 90) or (self.is_source() and rotation == 270): + y = self.get_parent().W + x = (PORT_SEPARATION+self.H)*index+offset + self.add_area((x, y), (self.H, self.W)) + self._connector_coordinate = (x+self.H/2, y+1+self.W) + #the connector length + self._connector_length = CONNECTOR_EXTENSION_MINIMAL + CONNECTOR_EXTENSION_INCREMENT*index + def modify_height(self, start_height): + type_dict = {'bus':(lambda a: a * 3)}; + + if self.get_type() in type_dict: + return type_dict[self.get_type()](start_height); + else: + return start_height; + + def create_labels(self): + """Create the labels for the socket.""" + Element.create_labels(self) + self._bg_color = Colors.get_color(self.get_color()) + #create the layout + layout = gtk.DrawingArea().create_pango_layout('') + layout.set_markup(Utils.parse_template(PORT_MARKUP_TMPL, port=self)) + self.w, self.h = layout.get_pixel_size() + self.W, self.H = 2*PORT_LABEL_PADDING+self.w, 2*PORT_LABEL_PADDING+self.h + self.H = self.modify_height(self.H); + #create the pixmap + pixmap = self.get_parent().get_parent().new_pixmap(self.w, self.h) + gc = pixmap.new_gc() + gc.set_foreground(self._bg_color) + pixmap.draw_rectangle(gc, True, 0, 0, self.w, self.h) + pixmap.draw_layout(gc, 0, 0, layout) + #create vertical and horizontal pixmaps + self.horizontal_label = pixmap + if self.is_vertical(): + self.vertical_label = self.get_parent().get_parent().new_pixmap(self.h, self.w) + Utils.rotate_pixmap(gc, self.horizontal_label, self.vertical_label) + + def draw(self, gc, window): + """ + Draw the socket with a label. + + Args: + gc: the graphics context + window: the gtk window to draw on + """ + Element.draw( + self, gc, window, bg_color=self._bg_color, + border_color=self.is_highlighted() and Colors.HIGHLIGHT_COLOR or Colors.BORDER_COLOR, + ) + X,Y = self.get_coordinate() + (x,y),(w,h) = self._areas_list[0] #use the first area's sizes to place the labels + if self.is_horizontal(): + window.draw_drawable(gc, self.horizontal_label, 0, 0, x+X+(self.W-self.w)/2, y+Y+(self.H-self.h)/2, -1, -1) + elif self.is_vertical(): + window.draw_drawable(gc, self.vertical_label, 0, 0, x+X+(self.H-self.h)/2, y+Y+(self.W-self.w)/2, -1, -1) + + def get_connector_coordinate(self): + """ + Get the coordinate where connections may attach to. + + Returns: + the connector coordinate (x, y) tuple + """ + x,y = self._connector_coordinate + X,Y = self.get_coordinate() + return (x+X, y+Y) + + def get_connector_direction(self): + """ + Get the direction that the socket points: 0,90,180,270. + This is the rotation degree if the socket is an output or + the rotation degree + 180 if the socket is an input. + + Returns: + the direction in degrees + """ + if self.is_source(): return self.get_rotation() + elif self.is_sink(): return (self.get_rotation() + 180)%360 + + def get_connector_length(self): + """ + Get the length of the connector. + The connector length increases as the port index changes. + + Returns: + the length in pixels + """ + return self._connector_length + + def get_rotation(self): + """ + Get the parent's rotation rather than self. + + Returns: + the parent's rotation + """ + return self.get_parent().get_rotation() + + def move(self, delta_coor): + """ + Move the parent rather than self. + + Args: + delta_corr: the (delta_x, delta_y) tuple + """ + self.get_parent().move(delta_coor) + + def rotate(self, direction): + """ + Rotate the parent rather than self. + + Args: + direction: degrees to rotate + """ + self.get_parent().rotate(direction) + + def get_coordinate(self): + """ + Get the parent's coordinate rather than self. + + Returns: + the parents coordinate + """ + return self.get_parent().get_coordinate() + + def set_highlighted(self, highlight): + """ + Set the parent highlight rather than self. + + Args: + highlight: true to enable highlighting + """ + self.get_parent().set_highlighted(highlight) + + def is_highlighted(self): + """ + Get the parent's is highlight rather than self. + + Returns: + the parent's highlighting status + """ + return self.get_parent().is_highlighted() diff --git a/grc/gui/Preferences.py b/grc/gui/Preferences.py index 1d89920dd5..ce545cab6a 100644 --- a/grc/gui/Preferences.py +++ b/grc/gui/Preferences.py @@ -27,60 +27,60 @@ def file_extension(): return '.'+_platform.get_key() def _prefs_file(): return os.path.join(os.path.expanduser('~'), file_extension()) def load(platform): - global _platform - _platform = platform - #create sections - _config_parser.add_section('main') - _config_parser.add_section('files_open') - try: _config_parser.read(_prefs_file()) - except: pass + global _platform + _platform = platform + #create sections + _config_parser.add_section('main') + _config_parser.add_section('files_open') + try: _config_parser.read(_prefs_file()) + except: pass def save(): - try: _config_parser.write(open(_prefs_file(), 'w')) - except: pass + try: _config_parser.write(open(_prefs_file(), 'w')) + except: pass ########################################################################### # Special methods for specific program functionalities ########################################################################### def main_window_size(size=None): - if size is not None: - _config_parser.set('main', 'main_window_width', size[0]) - _config_parser.set('main', 'main_window_height', size[1]) - else: - try: return ( - _config_parser.getint('main', 'main_window_width'), - _config_parser.getint('main', 'main_window_height'), - ) - except: return (1, 1) + if size is not None: + _config_parser.set('main', 'main_window_width', size[0]) + _config_parser.set('main', 'main_window_height', size[1]) + else: + try: return ( + _config_parser.getint('main', 'main_window_width'), + _config_parser.getint('main', 'main_window_height'), + ) + except: return (1, 1) def file_open(file=None): - if file is not None: _config_parser.set('main', 'file_open', file) - else: - try: return _config_parser.get('main', 'file_open') - except: return '' + if file is not None: _config_parser.set('main', 'file_open', file) + else: + try: return _config_parser.get('main', 'file_open') + except: return '' def files_open(files=None): - if files is not None: - _config_parser.remove_section('files_open') #clear section - _config_parser.add_section('files_open') - for i, file in enumerate(files): - _config_parser.set('files_open', 'file_open_%d'%i, file) - else: - files = list() - i = 0 - while True: - try: files.append(_config_parser.get('files_open', 'file_open_%d'%i)) - except: return files - i = i + 1 + if files is not None: + _config_parser.remove_section('files_open') #clear section + _config_parser.add_section('files_open') + for i, file in enumerate(files): + _config_parser.set('files_open', 'file_open_%d'%i, file) + else: + files = list() + i = 0 + while True: + try: files.append(_config_parser.get('files_open', 'file_open_%d'%i)) + except: return files + i = i + 1 def reports_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'reports_window_position', pos) - else: - try: return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 - except: return -1 + if pos is not None: _config_parser.set('main', 'reports_window_position', pos) + else: + try: return _config_parser.getint('main', 'reports_window_position') or 1 #greater than 0 + except: return -1 def blocks_window_position(pos=None): - if pos is not None: _config_parser.set('main', 'blocks_window_position', pos) - else: - try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 - except: return -1 + if pos is not None: _config_parser.set('main', 'blocks_window_position', pos) + else: + try: return _config_parser.getint('main', 'blocks_window_position') or 1 #greater than 0 + except: return -1 diff --git a/grc/gui/PropsDialog.py b/grc/gui/PropsDialog.py index 5264857fab..5c09f7cac1 100644 --- a/grc/gui/PropsDialog.py +++ b/grc/gui/PropsDialog.py @@ -25,158 +25,158 @@ from Dialogs import TextDisplay from Constants import MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT def get_title_label(title): - """ - Get a title label for the params window. - The title will be bold, underlined, and left justified. - - Args: - title: the text of the title - - Returns: - a gtk object - """ - label = gtk.Label() - label.set_markup('\n<b><span underline="low">%s</span>:</b>\n'%title) - hbox = gtk.HBox() - hbox.pack_start(label, False, False, padding=11) - return hbox + """ + Get a title label for the params window. + The title will be bold, underlined, and left justified. + + Args: + title: the text of the title + + Returns: + a gtk object + """ + label = gtk.Label() + label.set_markup('\n<b><span underline="low">%s</span>:</b>\n'%title) + hbox = gtk.HBox() + hbox.pack_start(label, False, False, padding=11) + return hbox class PropsDialog(gtk.Dialog): - """ - A dialog to set block parameters, view errors, and view documentation. - """ + """ + A dialog to set block parameters, view errors, and view documentation. + """ - def __init__(self, block): - """ - Properties dialog contructor. - - Args: - block: a block instance - """ - self._hash = 0 - LABEL_SPACING = 7 - gtk.Dialog.__init__(self, - title='Properties: %s'%block.get_name(), - buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), - ) - self._block = block - self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) - vbox = gtk.VBox() - #Create the scrolled window to hold all the parameters - scrolled_window = gtk.ScrolledWindow() - scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled_window.add_with_viewport(vbox) - self.vbox.pack_start(scrolled_window, True) - #Params box for block parameters - self._params_box = gtk.VBox() - self._params_box.pack_start(get_title_label('Parameters'), False) - self._input_object_params = list() - #Error Messages for the block - self._error_box = gtk.VBox() - self._error_messages_text_display = TextDisplay() - self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) - self._error_box.pack_start(get_title_label('Error Messages'), False) - self._error_box.pack_start(self._error_messages_text_display, False) - #Docs for the block - self._docs_box = err_box = gtk.VBox() - self._docs_text_display = TextDisplay() - self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) - self._docs_box.pack_start(get_title_label('Documentation'), False) - self._docs_box.pack_start(self._docs_text_display, False) - #Add the boxes - vbox.pack_start(self._params_box, False) - vbox.pack_start(self._error_box, False) - vbox.pack_start(self._docs_box, False) - #connect events - self.connect('key-press-event', self._handle_key_press) - self.connect('show', self._update_gui) - #show all (performs initial gui update) - self.show_all() + def __init__(self, block): + """ + Properties dialog contructor. + + Args: + block: a block instance + """ + self._hash = 0 + LABEL_SPACING = 7 + gtk.Dialog.__init__(self, + title='Properties: %s'%block.get_name(), + buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), + ) + self._block = block + self.set_size_request(MIN_DIALOG_WIDTH, MIN_DIALOG_HEIGHT) + vbox = gtk.VBox() + #Create the scrolled window to hold all the parameters + scrolled_window = gtk.ScrolledWindow() + scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_window.add_with_viewport(vbox) + self.vbox.pack_start(scrolled_window, True) + #Params box for block parameters + self._params_box = gtk.VBox() + self._params_box.pack_start(get_title_label('Parameters'), False) + self._input_object_params = list() + #Error Messages for the block + self._error_box = gtk.VBox() + self._error_messages_text_display = TextDisplay() + self._error_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) + self._error_box.pack_start(get_title_label('Error Messages'), False) + self._error_box.pack_start(self._error_messages_text_display, False) + #Docs for the block + self._docs_box = err_box = gtk.VBox() + self._docs_text_display = TextDisplay() + self._docs_box.pack_start(gtk.Label(), False, False, LABEL_SPACING) + self._docs_box.pack_start(get_title_label('Documentation'), False) + self._docs_box.pack_start(self._docs_text_display, False) + #Add the boxes + vbox.pack_start(self._params_box, False) + vbox.pack_start(self._error_box, False) + vbox.pack_start(self._docs_box, False) + #connect events + self.connect('key-press-event', self._handle_key_press) + self.connect('show', self._update_gui) + #show all (performs initial gui update) + self.show_all() - def _params_changed(self): - """ - Have the params in this dialog changed? - Ex: Added, removed, type change, hide change... - To the props dialog, the hide setting of 'none' and 'part' are identical. - Therfore, the props dialog only cares if the hide setting is/not 'all'. - Make a hash that uniquely represents the params' state. - - Returns: - true if changed - """ - old_hash = self._hash - #create a tuple of things from each param that affects the params box - self._hash = hash(tuple([( - hash(param), param.get_type(), param.get_hide() == 'all', - ) for param in self._block.get_params()])) - return self._hash != old_hash + def _params_changed(self): + """ + Have the params in this dialog changed? + Ex: Added, removed, type change, hide change... + To the props dialog, the hide setting of 'none' and 'part' are identical. + Therfore, the props dialog only cares if the hide setting is/not 'all'. + Make a hash that uniquely represents the params' state. + + Returns: + true if changed + """ + old_hash = self._hash + #create a tuple of things from each param that affects the params box + self._hash = hash(tuple([( + hash(param), param.get_type(), param.get_hide() == 'all', + ) for param in self._block.get_params()])) + return self._hash != old_hash - def _handle_changed(self, *args): - """ - A change occured within a param: - Rewrite/validate the block and update the gui. - """ - #update for the block - self._block.rewrite() - self._block.validate() - self._update_gui() + def _handle_changed(self, *args): + """ + A change occured within a param: + Rewrite/validate the block and update the gui. + """ + #update for the block + self._block.rewrite() + self._block.validate() + self._update_gui() - def _update_gui(self, *args): - """ - Repopulate the parameters box (if changed). - Update all the input parameters. - Update the error messages box. - Hide the box if there are no errors. - Update the documentation block. - Hide the box if there are no docs. - """ - #update the params box - if self._params_changed(): - #hide params box before changing - self._params_box.hide_all() - #empty the params box - for io_param in list(self._input_object_params): - self._params_box.remove(io_param) - self._input_object_params.remove(io_param) - io_param.destroy() - #repopulate the params box - for param in self._block.get_params(): - if param.get_hide() == 'all': continue - io_param = param.get_input(self._handle_changed) - self._input_object_params.append(io_param) - self._params_box.pack_start(io_param, False) - #show params box with new params - self._params_box.show_all() - #update the errors box - if self._block.is_valid(): self._error_box.hide() - else: self._error_box.show() - messages = '\n\n'.join(self._block.get_error_messages()) - self._error_messages_text_display.set_text(messages) - #update the docs box - if self._block.get_doc(): self._docs_box.show() - else: self._docs_box.hide() - self._docs_text_display.set_text(self._block.get_doc()) + def _update_gui(self, *args): + """ + Repopulate the parameters box (if changed). + Update all the input parameters. + Update the error messages box. + Hide the box if there are no errors. + Update the documentation block. + Hide the box if there are no docs. + """ + #update the params box + if self._params_changed(): + #hide params box before changing + self._params_box.hide_all() + #empty the params box + for io_param in list(self._input_object_params): + self._params_box.remove(io_param) + self._input_object_params.remove(io_param) + io_param.destroy() + #repopulate the params box + for param in self._block.get_params(): + if param.get_hide() == 'all': continue + io_param = param.get_input(self._handle_changed) + self._input_object_params.append(io_param) + self._params_box.pack_start(io_param, False) + #show params box with new params + self._params_box.show_all() + #update the errors box + if self._block.is_valid(): self._error_box.hide() + else: self._error_box.show() + messages = '\n\n'.join(self._block.get_error_messages()) + self._error_messages_text_display.set_text(messages) + #update the docs box + if self._block.get_doc(): self._docs_box.show() + else: self._docs_box.hide() + self._docs_text_display.set_text(self._block.get_doc()) - def _handle_key_press(self, widget, event): - """ - Handle key presses from the keyboard. - Call the ok response when enter is pressed. - - Returns: - false to forward the keypress - """ - if event.keyval == gtk.keysyms.Return: - self.response(gtk.RESPONSE_ACCEPT) - return True #handled here - return False #forward the keypress + def _handle_key_press(self, widget, event): + """ + Handle key presses from the keyboard. + Call the ok response when enter is pressed. + + Returns: + false to forward the keypress + """ + if event.keyval == gtk.keysyms.Return: + self.response(gtk.RESPONSE_ACCEPT) + return True #handled here + return False #forward the keypress - def run(self): - """ - Run the dialog and get its response. - - Returns: - true if the response was accept - """ - response = gtk.Dialog.run(self) - self.destroy() - return response == gtk.RESPONSE_ACCEPT + def run(self): + """ + Run the dialog and get its response. + + Returns: + true if the response was accept + """ + response = gtk.Dialog.run(self) + self.destroy() + return response == gtk.RESPONSE_ACCEPT diff --git a/grc/gui/StateCache.py b/grc/gui/StateCache.py index 50d85bf960..558f507716 100644 --- a/grc/gui/StateCache.py +++ b/grc/gui/StateCache.py @@ -21,82 +21,82 @@ import Actions from Constants import STATE_CACHE_SIZE class StateCache(object): - """ - The state cache is an interface to a list to record data/states and to revert to previous states. - States are recorded into the list in a circular fassion by using an index for the current state, - and counters for the range where states are stored. - """ + """ + The state cache is an interface to a list to record data/states and to revert to previous states. + States are recorded into the list in a circular fassion by using an index for the current state, + and counters for the range where states are stored. + """ - def __init__(self, initial_state): - """ - StateCache constructor. - - Args: - initial_state: the intial state (nested data) - """ - self.states = [None] * STATE_CACHE_SIZE #fill states - self.current_state_index = 0 - self.num_prev_states = 0 - self.num_next_states = 0 - self.states[0] = initial_state - self.update_actions() + def __init__(self, initial_state): + """ + StateCache constructor. + + Args: + initial_state: the intial state (nested data) + """ + self.states = [None] * STATE_CACHE_SIZE #fill states + self.current_state_index = 0 + self.num_prev_states = 0 + self.num_next_states = 0 + self.states[0] = initial_state + self.update_actions() - def save_new_state(self, state): - """ - Save a new state. - Place the new state at the next index and add one to the number of previous states. - - Args: - state: the new state - """ - self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE - self.states[self.current_state_index] = state - self.num_prev_states = self.num_prev_states + 1 - if self.num_prev_states == STATE_CACHE_SIZE: self.num_prev_states = STATE_CACHE_SIZE - 1 - self.num_next_states = 0 - self.update_actions() + def save_new_state(self, state): + """ + Save a new state. + Place the new state at the next index and add one to the number of previous states. + + Args: + state: the new state + """ + self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE + self.states[self.current_state_index] = state + self.num_prev_states = self.num_prev_states + 1 + if self.num_prev_states == STATE_CACHE_SIZE: self.num_prev_states = STATE_CACHE_SIZE - 1 + self.num_next_states = 0 + self.update_actions() - def get_current_state(self): - """ - Get the state at the current index. - - Returns: - the current state (nested data) - """ - self.update_actions() - return self.states[self.current_state_index] + def get_current_state(self): + """ + Get the state at the current index. + + Returns: + the current state (nested data) + """ + self.update_actions() + return self.states[self.current_state_index] - def get_prev_state(self): - """ - Get the previous state and decrement the current index. - - Returns: - the previous state or None - """ - if self.num_prev_states > 0: - self.current_state_index = (self.current_state_index + STATE_CACHE_SIZE -1)%STATE_CACHE_SIZE - self.num_next_states = self.num_next_states + 1 - self.num_prev_states = self.num_prev_states - 1 - return self.get_current_state() - return None + def get_prev_state(self): + """ + Get the previous state and decrement the current index. + + Returns: + the previous state or None + """ + if self.num_prev_states > 0: + self.current_state_index = (self.current_state_index + STATE_CACHE_SIZE -1)%STATE_CACHE_SIZE + self.num_next_states = self.num_next_states + 1 + self.num_prev_states = self.num_prev_states - 1 + return self.get_current_state() + return None - def get_next_state(self): - """ - Get the nest state and increment the current index. - - Returns: - the next state or None - """ - if self.num_next_states > 0: - self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE - self.num_next_states = self.num_next_states - 1 - self.num_prev_states = self.num_prev_states + 1 - return self.get_current_state() - return None + def get_next_state(self): + """ + Get the nest state and increment the current index. + + Returns: + the next state or None + """ + if self.num_next_states > 0: + self.current_state_index = (self.current_state_index + 1)%STATE_CACHE_SIZE + self.num_next_states = self.num_next_states - 1 + self.num_prev_states = self.num_prev_states + 1 + return self.get_current_state() + return None - def update_actions(self): - """ - Update the undo and redo actions based on the number of next and prev states. - """ - Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0) - Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0) + def update_actions(self): + """ + Update the undo and redo actions based on the number of next and prev states. + """ + Actions.FLOW_GRAPH_REDO.set_sensitive(self.num_next_states != 0) + Actions.FLOW_GRAPH_UNDO.set_sensitive(self.num_prev_states != 0) diff --git a/grc/gui/Utils.py b/grc/gui/Utils.py index b68c19c4e1..cc1f8ceb12 100644 --- a/grc/gui/Utils.py +++ b/grc/gui/Utils.py @@ -25,80 +25,86 @@ import gtk import gobject def rotate_pixmap(gc, src_pixmap, dst_pixmap, angle=gtk.gdk.PIXBUF_ROTATE_COUNTERCLOCKWISE): - """ - Load the destination pixmap with a rotated version of the source pixmap. - The source pixmap will be loaded into a pixbuf, rotated, and drawn to the destination pixmap. - The pixbuf is a client-side drawable, where a pixmap is a server-side drawable. - - Args: - gc: the graphics context - src_pixmap: the source pixmap - dst_pixmap: the destination pixmap - angle: the angle to rotate by - """ - width, height = src_pixmap.get_size() - pixbuf = gtk.gdk.Pixbuf( - colorspace=gtk.gdk.COLORSPACE_RGB, - has_alpha=False, bits_per_sample=8, - width=width, height=height, - ) - pixbuf.get_from_drawable(src_pixmap, src_pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) - pixbuf = pixbuf.rotate_simple(angle) - dst_pixmap.draw_pixbuf(gc, pixbuf, 0, 0, 0, 0) + """ + Load the destination pixmap with a rotated version of the source pixmap. + The source pixmap will be loaded into a pixbuf, rotated, and drawn to the destination pixmap. + The pixbuf is a client-side drawable, where a pixmap is a server-side drawable. + + Args: + gc: the graphics context + src_pixmap: the source pixmap + dst_pixmap: the destination pixmap + angle: the angle to rotate by + """ + width, height = src_pixmap.get_size() + pixbuf = gtk.gdk.Pixbuf( + colorspace=gtk.gdk.COLORSPACE_RGB, + has_alpha=False, bits_per_sample=8, + width=width, height=height, + ) + pixbuf.get_from_drawable(src_pixmap, src_pixmap.get_colormap(), 0, 0, 0, 0, -1, -1) + pixbuf = pixbuf.rotate_simple(angle) + dst_pixmap.draw_pixbuf(gc, pixbuf, 0, 0, 0, 0) def get_rotated_coordinate(coor, rotation): - """ - Rotate the coordinate by the given rotation. - - Args: - coor: the coordinate x, y tuple - rotation: the angle in degrees - - Returns: - the rotated coordinates - """ - #handles negative angles - rotation = (rotation + 360)%360 - if rotation not in POSSIBLE_ROTATIONS: - raise ValueError('unusable rotation angle "%s"'%str(rotation)) - #determine the number of degrees to rotate - cos_r, sin_r = { - 0: (1, 0), - 90: (0, 1), - 180: (-1, 0), - 270: (0, -1), - }[rotation] - x, y = coor - return (x*cos_r + y*sin_r, -x*sin_r + y*cos_r) + """ + Rotate the coordinate by the given rotation. + + Args: + coor: the coordinate x, y tuple + rotation: the angle in degrees + + Returns: + the rotated coordinates + """ + #handles negative angles + rotation = (rotation + 360)%360 + if rotation not in POSSIBLE_ROTATIONS: + raise ValueError('unusable rotation angle "%s"'%str(rotation)) + #determine the number of degrees to rotate + cos_r, sin_r = { + 0: (1, 0), + 90: (0, 1), + 180: (-1, 0), + 270: (0, -1), + }[rotation] + x, y = coor + return (x*cos_r + y*sin_r, -x*sin_r + y*cos_r) def get_angle_from_coordinates((x1,y1), (x2,y2)): - """ - Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees. - - Args: - (x1,y1): the coordinate of point 1 - (x2,y2): the coordinate of point 2 - - Returns: - the direction in degrees - """ - if y1 == y2:#0 or 180 - if x2 > x1: return 0 - else: return 180 - else:#90 or 270 - if y2 > y1: return 270 - else: return 90 + """ + Given two points, calculate the vector direction from point1 to point2, directions are multiples of 90 degrees. + + Args: + (x1,y1): the coordinate of point 1 + (x2,y2): the coordinate of point 2 + + Returns: + the direction in degrees + """ + if y1 == y2:#0 or 180 + if x2 > x1: return 0 + else: return 180 + else:#90 or 270 + if y2 > y1: return 270 + else: return 90 def parse_template(tmpl_str, **kwargs): - """ - Parse the template string with the given args. - Pass in the xml encode method for pango escape chars. - - Args: - tmpl_str: the template as a string - - Returns: - a string of the parsed template - """ - kwargs['encode'] = gobject.markup_escape_text - return str(Template(tmpl_str, kwargs)) + """ + Parse the template string with the given args. + Pass in the xml encode method for pango escape chars. + + Args: + tmpl_str: the template as a string + + Returns: + a string of the parsed template + """ + kwargs['encode'] = gobject.markup_escape_text + #try: + # cat = str(Template(tmpl_str, kwargs)) + #except TypeError: + # print 'guppy' + # print tmpl_str + # print str(kwargs['param'].get_error_messages()) + return str(Template(tmpl_str, kwargs)) diff --git a/grc/python/Block.py b/grc/python/Block.py index 806de46724..e13b26c12f 100644 --- a/grc/python/Block.py +++ b/grc/python/Block.py @@ -23,170 +23,187 @@ import extract_docs class Block(_Block, _GUIBlock): - def is_virtual_sink(self): return self.get_key() == 'virtual_sink' - def is_virtual_source(self): return self.get_key() == 'virtual_source' - - ##for make source to keep track of indexes - _source_count = 0 - ##for make sink to keep track of indexes - _sink_count = 0 - - def __init__(self, flow_graph, n): - """ - Make a new block from nested data. - - Args: - flow: graph the parent element - n: the nested odict - - Returns: - block a new block - """ - #grab the data - self._doc = n.find('doc') or '' - self._imports = map(lambda i: i.strip(), n.findall('import')) - self._make = n.find('make') - self._var_make = n.find('var_make') - self._checks = n.findall('check') - self._callbacks = n.findall('callback') - self._throttle = n.find('throttle') or '' - #build the block - _Block.__init__( - self, - flow_graph=flow_graph, - n=n, - ) - _GUIBlock.__init__(self) - - def throttle(self): return bool(self._throttle) - - def validate(self): - """ - Validate this block. - Call the base class validate. - Evaluate the checks: each check must evaluate to True. - """ - _Block.validate(self) - #evaluate the checks - for check in self._checks: - check_res = self.resolve_dependencies(check) - try: - if not self.get_parent().evaluate(check_res): - self.add_error_message('Check "%s" failed.'%check) - except: self.add_error_message('Check "%s" did not evaluate.'%check) - - def rewrite(self): - """ - Add and remove ports to adjust for the nports. - """ - _Block.rewrite(self) - - def rectify(ports): - #restore integer contiguity after insertion - #rectify the port names with the index - for i, port in enumerate(ports): - port._key = str(i) - port._name = port._n['name'] - if len(ports) > 1: port._name += str(i) - - def insert_port(get_ports, get_port, key): - prev_port = get_port(str(int(key)-1)) - get_ports().insert( - get_ports().index(prev_port)+1, - prev_port.copy(new_key=key), - ) - rectify(get_ports()) - - def remove_port(get_ports, get_port, key): - port = get_port(key) - for connection in port.get_connections(): - self.get_parent().remove_element(connection) - get_ports().remove(port) - rectify(get_ports()) - - #adjust nports - for get_ports, get_port in ( - (self.get_sources, self.get_source), - (self.get_sinks, self.get_sink), - ): - master_ports = filter(lambda p: p.get_nports(), get_ports()) - for i, master_port in enumerate(master_ports): - nports = master_port.get_nports() - index_first = get_ports().index(master_port) - try: index_last = get_ports().index(master_ports[i+1]) - except IndexError: index_last = len(get_ports()) - num_ports = index_last - index_first - #do nothing if nports is already num ports - if nports == num_ports: continue - #remove excess ports and connections - if nports < num_ports: - for key in reversed(map(str, range(index_first+nports, index_first+num_ports))): - remove_port(get_ports, get_port, key) - continue - #add more ports - if nports > num_ports: - for key in map(str, range(index_first+num_ports, index_first+nports)): - insert_port(get_ports, get_port, key) - continue - - def port_controller_modify(self, direction): - """ - Change the port controller. - - Args: - direction: +1 or -1 - - Returns: - true for change - """ - changed = False - #concat the nports string from the private nports settings of all ports - nports_str = ' '.join([port._nports for port in self.get_ports()]) - #modify all params whose keys appear in the nports string - for param in self.get_params(): - if param.is_enum() or param.get_key() not in nports_str: continue - #try to increment the port controller by direction - try: - value = param.get_evaluated() - value = value + direction - if 0 < value: - param.set_value(value) - changed = True - except: pass - return changed - - def get_doc(self): - doc = self._doc.strip('\n').replace('\\\n', '') - #merge custom doc with doxygen docs - return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') - - def get_category(self): - return _Block.get_category(self) - - def get_imports(self): - """ - Resolve all import statements. - Split each import statement at newlines. - Combine all import statments into a list. - Filter empty imports. - - Returns: - a list of import statements - """ - return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) - - def get_make(self): return self.resolve_dependencies(self._make) - def get_var_make(self): return self.resolve_dependencies(self._var_make) - - def get_callbacks(self): - """ - Get a list of function callbacks for this block. - - Returns: - a list of strings - """ - def make_callback(callback): - callback = self.resolve_dependencies(callback) - if 'self.' in callback: return callback - return 'self.%s.%s'%(self.get_id(), callback) - return map(make_callback, self._callbacks) + def is_virtual_sink(self): return self.get_key() == 'virtual_sink' + def is_virtual_source(self): return self.get_key() == 'virtual_source' + + ##for make source to keep track of indexes + _source_count = 0 + ##for make sink to keep track of indexes + _sink_count = 0 + + def __init__(self, flow_graph, n): + """ + Make a new block from nested data. + + Args: + flow: graph the parent element + n: the nested odict + + Returns: + block a new block + """ + #grab the data + self._doc = n.find('doc') or '' + self._imports = map(lambda i: i.strip(), n.findall('import')) + self._make = n.find('make') + self._var_make = n.find('var_make') + self._checks = n.findall('check') + self._callbacks = n.findall('callback') + self._throttle = n.find('throttle') or '' + self._bus_structure_source = n.find('bus_structure_source') or '' + self._bus_structure_sink = n.find('bus_structure_sink') or '' + #build the block + _Block.__init__( + self, + flow_graph=flow_graph, + n=n, + ) + _GUIBlock.__init__(self) + + def get_bus_structure(self, direction): + if direction == 'source': + bus_structure = self._bus_structure_source; + else: + bus_structure = self._bus_structure_sink; + + bus_structure = self.resolve_dependencies(bus_structure); + + if not bus_structure: return '' + try: + clean_bus_structure = self.get_parent().evaluate(bus_structure) + return clean_bus_structure + + except: return '' + def throttle(self): return bool(self._throttle) + + def validate(self): + """ + Validate this block. + Call the base class validate. + Evaluate the checks: each check must evaluate to True. + """ + _Block.validate(self) + #evaluate the checks + for check in self._checks: + check_res = self.resolve_dependencies(check) + try: + if not self.get_parent().evaluate(check_res): + self.add_error_message('Check "%s" failed.'%check) + except: self.add_error_message('Check "%s" did not evaluate.'%check) + + def rewrite(self): + """ + Add and remove ports to adjust for the nports. + """ + _Block.rewrite(self) + + def rectify(ports): + #restore integer contiguity after insertion + #rectify the port names with the index + self.back_ofthe_bus(ports); + for i, port in enumerate(ports): + port._key = str(i) + port._name = port._n['name'] + if len(ports) > 1 and not port._type == 'bus': port._name += str(i) + + def insert_port(get_ports, get_port, key): + prev_port = get_port(str(int(key)-1)) + get_ports().insert( + get_ports().index(prev_port)+1, + prev_port.copy(new_key=key), + ) + rectify(get_ports()) + + def remove_port(get_ports, get_port, key): + port = get_port(key) + for connection in port.get_connections(): + self.get_parent().remove_element(connection) + get_ports().remove(port) + rectify(get_ports()) + + #adjust nports + for get_ports, get_port in ( + (self.get_sources, self.get_source), + (self.get_sinks, self.get_sink), + ): + master_ports = filter(lambda p: p.get_nports(), get_ports()) + for i, master_port in enumerate(master_ports): + nports = master_port.get_nports() + index_first = get_ports().index(master_port) + try: index_last = get_ports().index(master_ports[i+1]) + except IndexError: index_last = len(get_ports()) + num_ports = index_last - index_first + #do nothing if nports is already num ports + if nports == num_ports: continue + #remove excess ports and connections + if nports < num_ports: + for key in reversed(map(str, range(index_first+nports, index_first+num_ports))): + remove_port(get_ports, get_port, key); + continue + #add more ports + if nports > num_ports: + for key in map(str, range(index_first+num_ports, index_first+nports)): + insert_port(get_ports, get_port, key) + continue + + def port_controller_modify(self, direction): + """ + Change the port controller. + + Args: + direction: +1 or -1 + + Returns: + true for change + """ + changed = False + #concat the nports string from the private nports settings of all ports + nports_str = ' '.join([port._nports for port in self.get_ports()]) + #modify all params whose keys appear in the nports string + for param in self.get_params(): + if param.is_enum() or param.get_key() not in nports_str: continue + #try to increment the port controller by direction + try: + value = param.get_evaluated() + value = value + direction + if 0 < value: + param.set_value(value) + changed = True + except: pass + return changed + + def get_doc(self): + doc = self._doc.strip('\n').replace('\\\n', '') + #merge custom doc with doxygen docs + return '\n'.join([doc, extract_docs.extract(self.get_key())]).strip('\n') + + def get_category(self): + return _Block.get_category(self) + + def get_imports(self): + """ + Resolve all import statements. + Split each import statement at newlines. + Combine all import statments into a list. + Filter empty imports. + + Returns: + a list of import statements + """ + return filter(lambda i: i, sum(map(lambda i: self.resolve_dependencies(i).split('\n'), self._imports), [])) + + def get_make(self): return self.resolve_dependencies(self._make) + def get_var_make(self): return self.resolve_dependencies(self._var_make) + + def get_callbacks(self): + """ + Get a list of function callbacks for this block. + + Returns: + a list of strings + """ + def make_callback(callback): + callback = self.resolve_dependencies(callback) + if 'self.' in callback: return callback + return 'self.%s.%s'%(self.get_id(), callback) + return map(make_callback, self._callbacks) diff --git a/grc/python/Connection.py b/grc/python/Connection.py index 341dd2d821..7f235b190b 100644 --- a/grc/python/Connection.py +++ b/grc/python/Connection.py @@ -24,23 +24,26 @@ from .. gui.Connection import Connection as _GUIConnection class Connection(_Connection, _GUIConnection): - def __init__(self, **kwargs): - _Connection.__init__(self, **kwargs) - _GUIConnection.__init__(self) - - def is_msg(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'msg' - - def is_message(self): - return self.get_source().get_type() == self.get_sink().get_type() == 'message' - - def validate(self): - """ - Validate the connections. - The ports must match in io size. - """ - Element.validate(self) - source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() - sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() - if source_size != sink_size: - self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) + def __init__(self, **kwargs): + _Connection.__init__(self, **kwargs) + _GUIConnection.__init__(self) + + def is_msg(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'msg' + + def is_message(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'message' + + def is_bus(self): + return self.get_source().get_type() == self.get_sink().get_type() == 'bus' + + def validate(self): + """ + Validate the connections. + The ports must match in io size. + """ + Element.validate(self) + source_size = Constants.TYPE_TO_SIZEOF[self.get_source().get_type()] * self.get_source().get_vlen() + sink_size = Constants.TYPE_TO_SIZEOF[self.get_sink().get_type()] * self.get_sink().get_vlen() + if source_size != sink_size: + self.add_error_message('Source IO size "%s" does not match sink IO size "%s".'%(source_size, sink_size)) diff --git a/grc/python/Constants.py b/grc/python/Constants.py index b8dc9a96a1..15cc203b43 100644 --- a/grc/python/Constants.py +++ b/grc/python/Constants.py @@ -27,11 +27,11 @@ _gr_prefs = gr.prefs() PATH_SEP = {'/':':', '\\':';'}[os.path.sep] HIER_BLOCKS_LIB_DIR = os.path.join(os.path.expanduser('~'), '.grc_gnuradio') BLOCKS_DIRS = filter( #filter blank strings - lambda x: x, PATH_SEP.join([ - os.environ.get('GRC_BLOCKS_PATH', ''), - _gr_prefs.get_string('grc', 'local_blocks_path', ''), - _gr_prefs.get_string('grc', 'global_blocks_path', ''), - ]).split(PATH_SEP), + lambda x: x, PATH_SEP.join([ + os.environ.get('GRC_BLOCKS_PATH', ''), + _gr_prefs.get_string('grc', 'local_blocks_path', ''), + _gr_prefs.get_string('grc', 'global_blocks_path', ''), + ]).split(PATH_SEP), ) + [HIER_BLOCKS_LIB_DIR] #file creation modes @@ -45,39 +45,40 @@ BLOCK_DTD = os.path.join(DATA_DIR, 'block.dtd') DEFAULT_FLOW_GRAPH = os.path.join(DATA_DIR, 'default_flow_graph.grc') CORE_TYPES = ( #name, key, sizeof, color - ('Complex Float 64', 'fc64', 16, '#CC8C69'), - ('Complex Float 32', 'fc32', 8, '#3399FF'), - ('Complex Integer 64', 'sc64', 16, '#66CC00'), - ('Complex Integer 32', 'sc32', 8, '#33cc66'), - ('Complex Integer 16', 'sc16', 4, '#cccc00'), - ('Complex Integer 8', 'sc8', 2, '#cc00cc'), - ('Float 64', 'f64', 8, '#66CCCC'), - ('Float 32', 'f32', 4, '#FF8C69'), - ('Integer 64', 's64', 8, '#99FF33'), - ('Integer 32', 's32', 4, '#00FF99'), - ('Integer 16', 's16', 2, '#FFFF66'), - ('Integer 8', 's8', 1, '#FF66FF'), - ('Message Queue', 'msg', 0, '#777777'), - ('Async Message', 'message', 0, '#C0C0C0'), - ('Wildcard', '', 0, '#FFFFFF'), + ('Complex Float 64', 'fc64', 16, '#CC8C69'), + ('Complex Float 32', 'fc32', 8, '#3399FF'), + ('Complex Integer 64', 'sc64', 16, '#66CC00'), + ('Complex Integer 32', 'sc32', 8, '#33cc66'), + ('Complex Integer 16', 'sc16', 4, '#cccc00'), + ('Complex Integer 8', 'sc8', 2, '#cc00cc'), + ('Float 64', 'f64', 8, '#66CCCC'), + ('Float 32', 'f32', 4, '#FF8C69'), + ('Integer 64', 's64', 8, '#99FF33'), + ('Integer 32', 's32', 4, '#00FF99'), + ('Integer 16', 's16', 2, '#FFFF66'), + ('Integer 8', 's8', 1, '#FF66FF'), + ('Message Queue', 'msg', 0, '#777777'), + ('Async Message', 'message', 0, '#C0C0C0'), + ('Bus Connection', 'bus', 0, '#FFFFFF'), + ('Wildcard', '', 0, '#FFFFFF'), ) ALIAS_TYPES = { - 'complex' : (8, '#3399FF'), - 'float' : (4, '#FF8C69'), - 'int' : (4, '#00FF99'), - 'short' : (2, '#FFFF66'), - 'byte' : (1, '#FF66FF'), + 'complex' : (8, '#3399FF'), + 'float' : (4, '#FF8C69'), + 'int' : (4, '#00FF99'), + 'short' : (2, '#FFFF66'), + 'byte' : (1, '#FF66FF'), } TYPE_TO_COLOR = dict() TYPE_TO_SIZEOF = dict() for name, key, sizeof, color in CORE_TYPES: - TYPE_TO_COLOR[key] = color - TYPE_TO_SIZEOF[key] = sizeof + TYPE_TO_COLOR[key] = color + TYPE_TO_SIZEOF[key] = sizeof for key, (sizeof, color) in ALIAS_TYPES.iteritems(): - TYPE_TO_COLOR[key] = color - TYPE_TO_SIZEOF[key] = sizeof + TYPE_TO_COLOR[key] = color + TYPE_TO_SIZEOF[key] = sizeof #coloring COMPLEX_COLOR_SPEC = '#3399FF' diff --git a/grc/python/FlowGraph.py b/grc/python/FlowGraph.py index 1080006cf5..180d605720 100644 --- a/grc/python/FlowGraph.py +++ b/grc/python/FlowGraph.py @@ -20,174 +20,237 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import expr_utils from .. base.FlowGraph import FlowGraph as _FlowGraph from .. gui.FlowGraph import FlowGraph as _GUIFlowGraph +from .. base.odict import odict import re _variable_matcher = re.compile('^(variable\w*)$') _parameter_matcher = re.compile('^(parameter)$') _monitors_searcher = re.compile('(ctrlport_monitor)') +_bussink_searcher = re.compile('^(bus_sink)$') +_bussrc_searcher = re.compile('^(bus_source)$') +_bus_struct_sink_searcher = re.compile('^(bus_structure_sink)$') +_bus_struct_src_searcher = re.compile('^(bus_structure_source)$') class FlowGraph(_FlowGraph, _GUIFlowGraph): - def __init__(self, **kwargs): - _FlowGraph.__init__(self, **kwargs) - _GUIFlowGraph.__init__(self) - self._eval_cache = dict() + def __init__(self, **kwargs): + _FlowGraph.__init__(self, **kwargs) + _GUIFlowGraph.__init__(self) + self._eval_cache = dict() - def _eval(self, code, namespace, namespace_hash): - """ - Evaluate the code with the given namespace. - - Args: - code: a string with python code - namespace: a dict representing the namespace - namespace_hash: a unique hash for the namespace - - Returns: - the resultant object - """ - if not code: raise Exception, 'Cannot evaluate empty statement.' - my_hash = hash(code) ^ namespace_hash - #cache if does not exist - if not self._eval_cache.has_key(my_hash): - self._eval_cache[my_hash] = eval(code, namespace, namespace) - #return from cache - return self._eval_cache[my_hash] - - def get_io_signaturev(self, direction): - """ - Get a list of io signatures for this flow graph. - - Args: - direction: a string of 'in' or 'out' - - Returns: - a list of dicts with: type, label, vlen, size - """ - sorted_pads = { - 'in': self.get_pad_sources(), - 'out': self.get_pad_sinks(), - }[direction] + def _eval(self, code, namespace, namespace_hash): + """ + Evaluate the code with the given namespace. + + Args: + code: a string with python code + namespace: a dict representing the namespace + namespace_hash: a unique hash for the namespace + + Returns: + the resultant object + """ + if not code: raise Exception, 'Cannot evaluate empty statement.' + my_hash = hash(code) ^ namespace_hash + #cache if does not exist + if not self._eval_cache.has_key(my_hash): + self._eval_cache[my_hash] = eval(code, namespace, namespace) + #return from cache + return self._eval_cache[my_hash] + + def get_io_signaturev(self, direction): + """ + Get a list of io signatures for this flow graph. + + Args: + direction: a string of 'in' or 'out' + + Returns: + a list of dicts with: type, label, vlen, size + """ + sorted_pads = { + 'in': self.get_pad_sources(), + 'out': self.get_pad_sinks(), + }[direction] # we only want stream ports - sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); - #load io signature - return [{ - 'label': str(pad.get_param('label').get_evaluated()), - 'type': str(pad.get_param('type').get_evaluated()), - 'vlen': str(pad.get_param('vlen').get_evaluated()), - 'size': pad.get_param('type').get_opt('size'), - 'optional': bool(pad.get_param('optional').get_evaluated()), - } for pad in sorted_pads] - - def get_pad_sources(self): - """ - Get a list of pad source blocks sorted by id order. - - Returns: - a list of pad source blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_pad_sinks(self): - """ - Get a list of pad sink blocks sorted by id order. - - Returns: - a list of pad sink blocks in this flow graph - """ - pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) - return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) - - def get_msg_pad_sources(self): - ps = self.get_pad_sources(); - return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); - - def get_msg_pad_sinks(self): - ps = self.get_pad_sinks(); - return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); - - def get_imports(self): - """ - Get a set of all import statments in this flow graph namespace. + sorted_pads = filter(lambda b: b.get_param('type').get_evaluated() != 'message', sorted_pads); + expanded_pads = []; + for i in sorted_pads: + for j in range(i.get_param('num_streams').get_evaluated()): + expanded_pads.append(i); + #load io signature + return [{ + 'label': str(pad.get_param('label').get_evaluated()), + 'type': str(pad.get_param('type').get_evaluated()), + 'vlen': str(pad.get_param('vlen').get_evaluated()), + 'size': pad.get_param('type').get_opt('size'), + 'optional': bool(pad.get_param('optional').get_evaluated()), + } for pad in expanded_pads] + + def get_pad_sources(self): + """ + Get a list of pad source blocks sorted by id order. + + Returns: + a list of pad source blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_source', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_pad_sinks(self): + """ + Get a list of pad sink blocks sorted by id order. + + Returns: + a list of pad sink blocks in this flow graph + """ + pads = filter(lambda b: b.get_key() == 'pad_sink', self.get_enabled_blocks()) + return sorted(pads, lambda x, y: cmp(x.get_id(), y.get_id())) + + def get_msg_pad_sources(self): + ps = self.get_pad_sources(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + + def get_msg_pad_sinks(self): + ps = self.get_pad_sinks(); + return filter(lambda b: b.get_param('type').get_evaluated() == 'message', ps); + + def get_imports(self): + """ + Get a set of all import statments in this flow graph namespace. + + Returns: + a set of import statements + """ + imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) + imports = sorted(set(imports)) + return imports + + def get_variables(self): + """ + Get a list of all variables in this flow graph namespace. + Exclude paramterized variables. + + Returns: + a sorted list of variable blocks in order of dependency (indep -> dep) + """ + variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) + return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) + + def get_parameters(self): + """ + Get a list of all paramterized variables in this flow graph namespace. + + Returns: + a list of paramterized variables + """ + parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) + return parameters + + def get_monitors(self): + """ + Get a list of all ControlPort monitors + """ + monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks()) + return monitors + + + def get_bussink(self): + bussink = filter(lambda b: _bussink_searcher.search(b.get_key()), self.get_enabled_blocks()) - Returns: - a set of import statements - """ - imports = sum([block.get_imports() for block in self.get_enabled_blocks()], []) - imports = sorted(set(imports)) - return imports - - def get_variables(self): - """ - Get a list of all variables in this flow graph namespace. - Exclude paramterized variables. + for i in bussink: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True; + + return False - Returns: - a sorted list of variable blocks in order of dependency (indep -> dep) - """ - variables = filter(lambda b: _variable_matcher.match(b.get_key()), self.get_enabled_blocks()) - return expr_utils.sort_objects(variables, lambda v: v.get_id(), lambda v: v.get_var_make()) - - def get_parameters(self): - """ - Get a list of all paramterized variables in this flow graph namespace. - Returns: - a list of paramterized variables - """ - parameters = filter(lambda b: _parameter_matcher.match(b.get_key()), self.get_enabled_blocks()) - return parameters - - def get_monitors(self): - """ - Get a list of all ControlPort monitors - """ - monitors = filter(lambda b: _monitors_searcher.search(b.get_key()), self.get_enabled_blocks()) - return monitors - - def rewrite(self): - """ - Flag the namespace to be renewed. - """ - self._renew_eval_ns = True - _FlowGraph.rewrite(self) - - def evaluate(self, expr): - """ - Evaluate the expression. + + def get_bussrc(self): + bussrc = filter(lambda b: _bussrc_searcher.search(b.get_key()), self.get_enabled_blocks()) + + for i in bussrc: + for j in i.get_params(): + if j.get_name() == 'On/Off' and j.get_value() == 'on': + return True; + + return False + + def get_bus_structure_sink(self): + bussink = filter(lambda b: _bus_struct_sink_searcher.search(b.get_key()), self.get_enabled_blocks()) + + return bussink + + def get_bus_structure_src(self): + bussrc = filter(lambda b: _bus_struct_src_searcher.search(b.get_key()), self.get_enabled_blocks()) - Args: - expr: the string expression - @throw Exception bad expression + return bussrc - Returns: - the evaluated data - """ - if self._renew_eval_ns: - self._renew_eval_ns = False - #reload namespace - n = dict() - #load imports - for imp in self.get_imports(): - try: exec imp in n - except: pass - #load parameters - np = dict() - for parameter in self.get_parameters(): - try: - e = eval(parameter.get_param('value').to_code(), n, n) - np[parameter.get_id()] = e - except: pass - n.update(np) #merge param namespace - #load variables - for variable in self.get_variables(): - try: - e = eval(variable.get_param('value').to_code(), n, n) - n[variable.get_id()] = e - except: pass - #make namespace public - self.n = n - self.n_hash = hash(str(n)) - #evaluate - e = self._eval(expr, self.n, self.n_hash) - return e + + def rewrite(self): + """ + Flag the namespace to be renewed. + """ + def reconnect_bus_blocks(): + for block in self.get_blocks(): + + if 'bus' in map(lambda a: a.get_type(), block.get_sources_gui()): + + + for i in range(len(block.get_sources_gui())): + if len(block.get_sources_gui()[i].get_connections()) > 0: + source = block.get_sources_gui()[i] + sink = [] + + for j in range(len(source.get_connections())): + sink.append(source.get_connections()[j].get_sink()); + + + for elt in source.get_connections(): + self.remove_element(elt); + for j in sink: + self.connect(source, j); + self._renew_eval_ns = True + _FlowGraph.rewrite(self); + reconnect_bus_blocks(); + + def evaluate(self, expr): + """ + Evaluate the expression. + + Args: + expr: the string expression + @throw Exception bad expression + + Returns: + the evaluated data + """ + if self._renew_eval_ns: + self._renew_eval_ns = False + #reload namespace + n = dict() + #load imports + for imp in self.get_imports(): + try: exec imp in n + except: pass + #load parameters + np = dict() + for parameter in self.get_parameters(): + try: + e = eval(parameter.get_param('value').to_code(), n, n) + np[parameter.get_id()] = e + except: pass + n.update(np) #merge param namespace + #load variables + for variable in self.get_variables(): + try: + e = eval(variable.get_param('value').to_code(), n, n) + n[variable.get_id()] = e + except: pass + #make namespace public + self.n = n + self.n_hash = hash(str(n)) + #evaluate + e = self._eval(expr, self.n, self.n_hash) + return e diff --git a/grc/python/Generator.py b/grc/python/Generator.py index 77abc45281..005ed4c2b3 100644 --- a/grc/python/Generator.py +++ b/grc/python/Generator.py @@ -24,137 +24,137 @@ import tempfile from Cheetah.Template import Template import expr_utils from Constants import \ - TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, \ - HIER_BLOCKS_LIB_DIR, FLOW_GRAPH_TEMPLATE + TOP_BLOCK_FILE_MODE, HIER_BLOCK_FILE_MODE, \ + HIER_BLOCKS_LIB_DIR, FLOW_GRAPH_TEMPLATE import convert_hier from .. gui import Messages class Generator(object): - def __init__(self, flow_graph, file_path): - """ - Initialize the generator object. - Determine the file to generate. - - Args: - flow_graph: the flow graph object - file_path: the path to write the file to - """ - self._flow_graph = flow_graph - self._generate_options = self._flow_graph.get_option('generate_options') - if self._generate_options == 'hb': - self._mode = HIER_BLOCK_FILE_MODE - dirname = HIER_BLOCKS_LIB_DIR - else: - self._mode = TOP_BLOCK_FILE_MODE - dirname = os.path.dirname(file_path) - #handle the case where the directory is read-only - #in this case, use the system's temp directory - if not os.access(dirname, os.W_OK): - dirname = tempfile.gettempdir() - filename = self._flow_graph.get_option('id') + '.py' - self._file_path = os.path.join(dirname, filename) + def __init__(self, flow_graph, file_path): + """ + Initialize the generator object. + Determine the file to generate. + + Args: + flow_graph: the flow graph object + file_path: the path to write the file to + """ + self._flow_graph = flow_graph + self._generate_options = self._flow_graph.get_option('generate_options') + if self._generate_options == 'hb': + self._mode = HIER_BLOCK_FILE_MODE + dirname = HIER_BLOCKS_LIB_DIR + else: + self._mode = TOP_BLOCK_FILE_MODE + dirname = os.path.dirname(file_path) + #handle the case where the directory is read-only + #in this case, use the system's temp directory + if not os.access(dirname, os.W_OK): + dirname = tempfile.gettempdir() + filename = self._flow_graph.get_option('id') + '.py' + self._file_path = os.path.join(dirname, filename) - def get_file_path(self): return self._file_path + def get_file_path(self): return self._file_path - def write(self): - #do throttle warning - throttled = any(map(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks())) - if not throttled and self._generate_options != 'hb': - Messages.send_warning('''\ + def write(self): + #do throttle warning + throttled = any(map(lambda b: b.throttle(), self._flow_graph.get_enabled_blocks())) + if not throttled and self._generate_options != 'hb': + Messages.send_warning('''\ This flow graph may not have flow control: no audio or usrp blocks found. \ Add a Misc->Throttle block to your flow graph to avoid CPU congestion.''') - #generate - open(self.get_file_path(), 'w').write(str(self)) - if self._generate_options == 'hb': - #convert hier block to xml wrapper - convert_hier.convert_hier(self._flow_graph, self.get_file_path()) - os.chmod(self.get_file_path(), self._mode) + #generate + open(self.get_file_path(), 'w').write(str(self)) + if self._generate_options == 'hb': + #convert hier block to xml wrapper + convert_hier.convert_hier(self._flow_graph, self.get_file_path()) + os.chmod(self.get_file_path(), self._mode) - def get_popen(self): - """ - Execute this python flow graph. - - Returns: - a popen object - """ - #extract the path to the python executable - python_exe = sys.executable + def get_popen(self): + """ + Execute this python flow graph. + + Returns: + a popen object + """ + #extract the path to the python executable + python_exe = sys.executable - #when using wx gui on mac os, execute with pythonw - #using pythonw is not necessary anymore, disabled below - #if self._generate_options == 'wx_gui' and 'darwin' in sys.platform.lower(): - # python_exe = 'pythonw' + #when using wx gui on mac os, execute with pythonw + #using pythonw is not necessary anymore, disabled below + #if self._generate_options == 'wx_gui' and 'darwin' in sys.platform.lower(): + # python_exe = 'pythonw' - #setup the command args to run - cmds = [python_exe, '-u', self.get_file_path()] #-u is unbuffered stdio + #setup the command args to run + cmds = [python_exe, '-u', self.get_file_path()] #-u is unbuffered stdio - #when in no gui mode on linux, use an xterm (looks nice) - if self._generate_options == 'no_gui' and 'linux' in sys.platform.lower(): - cmds = ['xterm', '-e'] + cmds + #when in no gui mode on linux, use an xterm (looks nice) + if self._generate_options == 'no_gui' and 'linux' in sys.platform.lower(): + cmds = ['xterm', '-e'] + cmds - p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True) - return p + p = subprocess.Popen(args=cmds, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=False, universal_newlines=True) + return p - def __str__(self): - """ - Convert the flow graph to python code. - - Returns: - a string of python code - """ - title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() - imports = self._flow_graph.get_imports() - variables = self._flow_graph.get_variables() - parameters = self._flow_graph.get_parameters() - monitors = self._flow_graph.get_monitors() - #list of blocks not including variables and imports and parameters and disabled - def _get_block_sort_text(block): - code = block.get_make().replace(block.get_id(), ' ') - try: code += block.get_param('notebook').get_value() #older gui markup w/ wxgui - except: pass - try: code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui - except: pass - return code - blocks = expr_utils.sort_objects( - self._flow_graph.get_enabled_blocks(), - lambda b: b.get_id(), _get_block_sort_text - ) - #list of regular blocks (all blocks minus the special ones) - blocks = filter(lambda b: b not in (imports + parameters), blocks) - #list of connections where each endpoint is enabled - connections = filter(lambda c: not (c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) - messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) - messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) - #list of variable names - var_ids = [var.get_id() for var in parameters + variables] - #prepend self. - replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids]) - #list of callbacks - callbacks = [ - expr_utils.expr_replace(cb, replace_dict) - for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) - ] - #map var id to callbacks - var_id2cbs = dict( - [(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) - for var_id in var_ids] - ) - #load the namespace - namespace = { - 'title': title, - 'imports': imports, - 'flow_graph': self._flow_graph, - 'variables': variables, - 'parameters': parameters, + def __str__(self): + """ + Convert the flow graph to python code. + + Returns: + a string of python code + """ + title = self._flow_graph.get_option('title') or self._flow_graph.get_option('id').replace('_', ' ').title() + imports = self._flow_graph.get_imports() + variables = self._flow_graph.get_variables() + parameters = self._flow_graph.get_parameters() + monitors = self._flow_graph.get_monitors() + #list of blocks not including variables and imports and parameters and disabled + def _get_block_sort_text(block): + code = block.get_make().replace(block.get_id(), ' ') + try: code += block.get_param('notebook').get_value() #older gui markup w/ wxgui + except: pass + try: code += block.get_param('gui_hint').get_value() #newer gui markup w/ qtgui + except: pass + return code + blocks = expr_utils.sort_objects( + self._flow_graph.get_enabled_blocks(), + lambda b: b.get_id(), _get_block_sort_text + ) + #list of regular blocks (all blocks minus the special ones) + blocks = filter(lambda b: b not in (imports + parameters), blocks) + #list of connections where each endpoint is enabled + connections = filter(lambda c: not (c.is_bus() or c.is_msg() or c.is_message()), self._flow_graph.get_enabled_connections()) + messages = filter(lambda c: c.is_msg(), self._flow_graph.get_enabled_connections()) + messages2 = filter(lambda c: c.is_message(), self._flow_graph.get_enabled_connections()) + #list of variable names + var_ids = [var.get_id() for var in parameters + variables] + #prepend self. + replace_dict = dict([(var_id, 'self.%s'%var_id) for var_id in var_ids]) + #list of callbacks + callbacks = [ + expr_utils.expr_replace(cb, replace_dict) + for cb in sum([block.get_callbacks() for block in self._flow_graph.get_enabled_blocks()], []) + ] + #map var id to callbacks + var_id2cbs = dict( + [(var_id, filter(lambda c: expr_utils.get_variable_dependencies(c, [var_id]), callbacks)) + for var_id in var_ids] + ) + #load the namespace + namespace = { + 'title': title, + 'imports': imports, + 'flow_graph': self._flow_graph, + 'variables': variables, + 'parameters': parameters, 'monitors': monitors, - 'blocks': blocks, - 'connections': connections, - 'messages': messages, - 'messages2': messages2, - 'generate_options': self._generate_options, - 'var_id2cbs': var_id2cbs, - } - #build the template - t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) - return str(t) + 'blocks': blocks, + 'connections': connections, + 'messages': messages, + 'messages2': messages2, + 'generate_options': self._generate_options, + 'var_id2cbs': var_id2cbs, + } + #build the template + t = Template(open(FLOW_GRAPH_TEMPLATE, 'r').read(), namespace) + return str(t) diff --git a/grc/python/Param.py b/grc/python/Param.py index 696f16cc94..e603d6cdbe 100644 --- a/grc/python/Param.py +++ b/grc/python/Param.py @@ -34,51 +34,51 @@ _check_id_matcher = re.compile('^[a-z|A-Z]\w*$') _show_id_matcher = re.compile('^(variable\w*|parameter|options|notebook)$') class FileParam(EntryParam): - """Provide an entry box for filename and a button to browse for a file.""" + """Provide an entry box for filename and a button to browse for a file.""" - def __init__(self, *args, **kwargs): - EntryParam.__init__(self, *args, **kwargs) - input = gtk.Button('...') - input.connect('clicked', self._handle_clicked) - self.pack_start(input, False) + def __init__(self, *args, **kwargs): + EntryParam.__init__(self, *args, **kwargs) + input = gtk.Button('...') + input.connect('clicked', self._handle_clicked) + self.pack_start(input, False) - def _handle_clicked(self, widget=None): - """ - If the button was clicked, open a file dialog in open/save format. - Replace the text in the entry with the new filename from the file dialog. - """ - #get the paths - file_path = self.param.is_valid() and self.param.get_evaluated() or '' - (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '') - if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths - #build the dialog - if self.param.get_type() == 'file_open': - file_dialog = gtk.FileChooserDialog('Open a Data File...', None, - gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK)) - elif self.param.get_type() == 'file_save': - file_dialog = gtk.FileChooserDialog('Save a Data File...', None, - gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK)) - file_dialog.set_do_overwrite_confirmation(True) - file_dialog.set_current_name(basename) #show the current filename - file_dialog.set_current_folder(dirname) #current directory - file_dialog.set_select_multiple(False) - file_dialog.set_local_only(True) - if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog - file_path = file_dialog.get_filename() #get the file path - self._input.set_text(file_path) - self._handle_changed() - file_dialog.destroy() #destroy the dialog + def _handle_clicked(self, widget=None): + """ + If the button was clicked, open a file dialog in open/save format. + Replace the text in the entry with the new filename from the file dialog. + """ + #get the paths + file_path = self.param.is_valid() and self.param.get_evaluated() or '' + (dirname, basename) = os.path.isfile(file_path) and os.path.split(file_path) or (file_path, '') + if not os.path.exists(dirname): dirname = os.getcwd() #fix bad paths + #build the dialog + if self.param.get_type() == 'file_open': + file_dialog = gtk.FileChooserDialog('Open a Data File...', None, + gtk.FILE_CHOOSER_ACTION_OPEN, ('gtk-cancel',gtk.RESPONSE_CANCEL,'gtk-open',gtk.RESPONSE_OK)) + elif self.param.get_type() == 'file_save': + file_dialog = gtk.FileChooserDialog('Save a Data File...', None, + gtk.FILE_CHOOSER_ACTION_SAVE, ('gtk-cancel',gtk.RESPONSE_CANCEL, 'gtk-save',gtk.RESPONSE_OK)) + file_dialog.set_do_overwrite_confirmation(True) + file_dialog.set_current_name(basename) #show the current filename + file_dialog.set_current_folder(dirname) #current directory + file_dialog.set_select_multiple(False) + file_dialog.set_local_only(True) + if gtk.RESPONSE_OK == file_dialog.run(): #run the dialog + file_path = file_dialog.get_filename() #get the file path + self._input.set_text(file_path) + self._handle_changed() + file_dialog.destroy() #destroy the dialog #blacklist certain ids, its not complete, but should help import __builtin__ ID_BLACKLIST = ['self', 'options', 'gr', 'blks2', 'wxgui', 'wx', 'math', 'forms', 'firdes'] + \ - filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__) + filter(lambda x: not x.startswith('_'), dir(gr.top_block())) + dir(__builtin__) #define types, native python + numpy VECTOR_TYPES = (tuple, list, set, numpy.ndarray) COMPLEX_TYPES = [complex, numpy.complex, numpy.complex64, numpy.complex128] REAL_TYPES = [float, numpy.float, numpy.float32, numpy.float64] INT_TYPES = [int, long, numpy.int, numpy.int8, numpy.int16, numpy.int32, numpy.uint64, - numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] + numpy.uint, numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64] #cast to tuple for isinstance, concat subtypes COMPLEX_TYPES = tuple(COMPLEX_TYPES + REAL_TYPES + INT_TYPES) REAL_TYPES = tuple(REAL_TYPES + INT_TYPES) @@ -86,389 +86,389 @@ INT_TYPES = tuple(INT_TYPES) class Param(_Param, _GUIParam): - def __init__(self, **kwargs): - _Param.__init__(self, **kwargs) - _GUIParam.__init__(self) - self._init = False - self._hostage_cells = list() + def __init__(self, **kwargs): + _Param.__init__(self, **kwargs) + _GUIParam.__init__(self) + self._init = False + self._hostage_cells = list() - def get_types(self): return ( - 'raw', 'enum', - 'complex', 'real', 'float', 'int', - 'complex_vector', 'real_vector', 'float_vector', 'int_vector', - 'hex', 'string', 'bool', - 'file_open', 'file_save', - 'id', 'stream_id', - 'grid_pos', 'notebook', 'gui_hint', - 'import', - ) + def get_types(self): return ( + 'raw', 'enum', + 'complex', 'real', 'float', 'int', + 'complex_vector', 'real_vector', 'float_vector', 'int_vector', + 'hex', 'string', 'bool', + 'file_open', 'file_save', + 'id', 'stream_id', + 'grid_pos', 'notebook', 'gui_hint', + 'import', + ) - def __repr__(self): - """ - Get the repr (nice string format) for this param. - - Returns: - the string representation - """ - ################################################## - # truncate helper method - ################################################## - def _truncate(string, style=0): - max_len = max(27 - len(self.get_name()), 3) - if len(string) > max_len: - if style < 0: #front truncate - string = '...' + string[3-max_len:] - elif style == 0: #center truncate - string = string[:max_len/2 -3] + '...' + string[-max_len/2:] - elif style > 0: #rear truncate - string = string[:max_len-3] + '...' - return string - ################################################## - # simple conditions - ################################################## - if not self.is_valid(): return _truncate(self.get_value()) - if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name() - ################################################## - # display logic for numbers - ################################################## - def num_to_str(num): - if isinstance(num, COMPLEX_TYPES): - num = complex(num) #cast to python complex - if num == 0: return '0' #value is zero - elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real - elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary - elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag))) - else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag)) - else: return str(num) - ################################################## - # split up formatting by type - ################################################## - truncate = 0 #default center truncate - e = self.get_evaluated() - t = self.get_type() - if isinstance(e, bool): return str(e) - elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) - elif isinstance(e, VECTOR_TYPES): #vector types - if len(e) > 8: - dt_str = self.get_value() #large vectors use code - truncate = 1 - else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval - elif t in ('file_open', 'file_save'): - dt_str = self.get_value() - truncate = -1 - else: dt_str = str(e) #other types - ################################################## - # done - ################################################## - return _truncate(dt_str, truncate) + def __repr__(self): + """ + Get the repr (nice string format) for this param. + + Returns: + the string representation + """ + ################################################## + # truncate helper method + ################################################## + def _truncate(string, style=0): + max_len = max(27 - len(self.get_name()), 3) + if len(string) > max_len: + if style < 0: #front truncate + string = '...' + string[3-max_len:] + elif style == 0: #center truncate + string = string[:max_len/2 -3] + '...' + string[-max_len/2:] + elif style > 0: #rear truncate + string = string[:max_len-3] + '...' + return string + ################################################## + # simple conditions + ################################################## + if not self.is_valid(): return _truncate(self.get_value()) + if self.get_value() in self.get_option_keys(): return self.get_option(self.get_value()).get_name() + ################################################## + # display logic for numbers + ################################################## + def num_to_str(num): + if isinstance(num, COMPLEX_TYPES): + num = complex(num) #cast to python complex + if num == 0: return '0' #value is zero + elif num.imag == 0: return '%s'%eng_notation.num_to_str(num.real) #value is real + elif num.real == 0: return '%sj'%eng_notation.num_to_str(num.imag) #value is imaginary + elif num.imag < 0: return '%s-%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(abs(num.imag))) + else: return '%s+%sj'%(eng_notation.num_to_str(num.real), eng_notation.num_to_str(num.imag)) + else: return str(num) + ################################################## + # split up formatting by type + ################################################## + truncate = 0 #default center truncate + e = self.get_evaluated() + t = self.get_type() + if isinstance(e, bool): return str(e) + elif isinstance(e, COMPLEX_TYPES): dt_str = num_to_str(e) + elif isinstance(e, VECTOR_TYPES): #vector types + if len(e) > 8: + dt_str = self.get_value() #large vectors use code + truncate = 1 + else: dt_str = ', '.join(map(num_to_str, e)) #small vectors use eval + elif t in ('file_open', 'file_save'): + dt_str = self.get_value() + truncate = -1 + else: dt_str = str(e) #other types + ################################################## + # done + ################################################## + return _truncate(dt_str, truncate) - def get_input(self, *args, **kwargs): - if self.get_type() in ('file_open', 'file_save'): return FileParam(self, *args, **kwargs) - return _GUIParam.get_input(self, *args, **kwargs) + def get_input(self, *args, **kwargs): + if self.get_type() in ('file_open', 'file_save'): return FileParam(self, *args, **kwargs) + return _GUIParam.get_input(self, *args, **kwargs) - def get_color(self): - """ - Get the color that represents this param's type. - - Returns: - a hex color code. - """ - try: - return { - #number types - 'complex': Constants.COMPLEX_COLOR_SPEC, - 'real': Constants.FLOAT_COLOR_SPEC, - 'float': Constants.FLOAT_COLOR_SPEC, - 'int': Constants.INT_COLOR_SPEC, - #vector types - 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, - 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, + def get_color(self): + """ + Get the color that represents this param's type. + + Returns: + a hex color code. + """ + try: + return { + #number types + 'complex': Constants.COMPLEX_COLOR_SPEC, + 'real': Constants.FLOAT_COLOR_SPEC, + 'float': Constants.FLOAT_COLOR_SPEC, + 'int': Constants.INT_COLOR_SPEC, + #vector types + 'complex_vector': Constants.COMPLEX_VECTOR_COLOR_SPEC, + 'real_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, 'float_vector': Constants.FLOAT_VECTOR_COLOR_SPEC, - 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, - #special - 'bool': Constants.INT_COLOR_SPEC, - 'hex': Constants.INT_COLOR_SPEC, - 'string': Constants.BYTE_VECTOR_COLOR_SPEC, - 'id': Constants.ID_COLOR_SPEC, - 'stream_id': Constants.ID_COLOR_SPEC, - 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, - 'notebook': Constants.INT_VECTOR_COLOR_SPEC, - 'raw': Constants.WILDCARD_COLOR_SPEC, - }[self.get_type()] - except: return _Param.get_color(self) + 'int_vector': Constants.INT_VECTOR_COLOR_SPEC, + #special + 'bool': Constants.INT_COLOR_SPEC, + 'hex': Constants.INT_COLOR_SPEC, + 'string': Constants.BYTE_VECTOR_COLOR_SPEC, + 'id': Constants.ID_COLOR_SPEC, + 'stream_id': Constants.ID_COLOR_SPEC, + 'grid_pos': Constants.INT_VECTOR_COLOR_SPEC, + 'notebook': Constants.INT_VECTOR_COLOR_SPEC, + 'raw': Constants.WILDCARD_COLOR_SPEC, + }[self.get_type()] + except: return _Param.get_color(self) - def get_hide(self): - """ - Get the hide value from the base class. - Hide the ID parameter for most blocks. Exceptions below. - If the parameter controls a port type, vlen, or nports, return part. - If the parameter is an empty grid position, return part. - These parameters are redundant to display in the flow graph view. - - Returns: - hide the hide property string - """ - hide = _Param.get_hide(self) - if hide: return hide - #hide ID in non variable blocks - if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' - #hide port controllers for type and nports - if self.get_key() in ' '.join(map( - lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) - ): return 'part' - #hide port controllers for vlen, when == 1 - if self.get_key() in ' '.join(map( - lambda p: p._vlen, self.get_parent().get_ports()) - ): - try: - if int(self.get_evaluated()) == 1: return 'part' - except: pass - #hide empty grid positions - if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part' - return hide + def get_hide(self): + """ + Get the hide value from the base class. + Hide the ID parameter for most blocks. Exceptions below. + If the parameter controls a port type, vlen, or nports, return part. + If the parameter is an empty grid position, return part. + These parameters are redundant to display in the flow graph view. + + Returns: + hide the hide property string + """ + hide = _Param.get_hide(self) + if hide: return hide + #hide ID in non variable blocks + if self.get_key() == 'id' and not _show_id_matcher.match(self.get_parent().get_key()): return 'part' + #hide port controllers for type and nports + if self.get_key() in ' '.join(map( + lambda p: ' '.join([p._type, p._nports]), self.get_parent().get_ports()) + ): return 'part' + #hide port controllers for vlen, when == 1 + if self.get_key() in ' '.join(map( + lambda p: p._vlen, self.get_parent().get_ports()) + ): + try: + if int(self.get_evaluated()) == 1: return 'part' + except: pass + #hide empty grid positions + if self.get_key() in ('grid_pos', 'notebook') and not self.get_value(): return 'part' + return hide - def validate(self): - """ - Validate the param. - A test evaluation is performed - """ - _Param.validate(self) #checks type - self._evaluated = None - try: self._evaluated = self.evaluate() - except Exception, e: self.add_error_message(str(e)) + def validate(self): + """ + Validate the param. + A test evaluation is performed + """ + _Param.validate(self) #checks type + self._evaluated = None + try: self._evaluated = self.evaluate() + except Exception, e: self.add_error_message(str(e)) - def get_evaluated(self): return self._evaluated + def get_evaluated(self): return self._evaluated - def evaluate(self): - """ - Evaluate the value. - - Returns: - evaluated type - """ - self._init = True - self._lisitify_flag = False - self._stringify_flag = False - self._hostage_cells = list() - def eval_string(v): - try: - e = self.get_parent().get_parent().evaluate(v) - if isinstance(e, str): return e - raise Exception #want to stringify - except: - self._stringify_flag = True - return v - t = self.get_type() - v = self.get_value() - ######################### - # Enum Type - ######################### - if self.is_enum(): return v - ######################### - # Numeric Types - ######################### - elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'raw': return e - elif t == 'complex': - if not isinstance(e, COMPLEX_TYPES): - raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) - return e - elif t == 'real' or t == 'float': - if not isinstance(e, REAL_TYPES): - raise Exception, 'Expression "%s" is invalid for type float.'%str(e) - return e - elif t == 'int': - if not isinstance(e, INT_TYPES): - raise Exception, 'Expression "%s" is invalid for type integer.'%str(e) - return e - elif t == 'hex': return hex(e) - elif t == 'bool': - if not isinstance(e, bool): - raise Exception, 'Expression "%s" is invalid for type bool.'%str(e) - return e - else: raise TypeError, 'Type "%s" not handled'%t - ######################### - # Numeric Vector Types - ######################### - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): - if not v: v = '()' #turn a blank string into an empty list, so it will eval - #raise exception if python cannot evaluate this value - try: e = self.get_parent().get_parent().evaluate(v) - except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) - #raise an exception if the data is invalid - if t == 'complex_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) - return e - elif t == 'real_vector' or t == 'float_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, REAL_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e) - return e - elif t == 'int_vector': - if not isinstance(e, VECTOR_TYPES): - self._lisitify_flag = True - e = [e] - if not all([isinstance(ei, INT_TYPES) for ei in e]): - raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e) - return e - ######################### - # String Types - ######################### - elif t in ('string', 'file_open', 'file_save'): - #do not check if file/directory exists, that is a runtime issue - e = eval_string(v) - return str(e) - ######################### - # Unique ID Type - ######################### - elif t == 'id': - #can python use this as a variable? - if not _check_id_matcher.match(v): - raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v - ids = [param.get_value() for param in self.get_all_params(t)] - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'ID "%s" is not unique.'%v - if v in ID_BLACKLIST: - raise Exception, 'ID "%s" is blacklisted.'%v - return v - ######################### - # Stream ID Type - ######################### - elif t == 'stream_id': - #get a list of all stream ids used in the virtual sinks - ids = [param.get_value() for param in filter( - lambda p: p.get_parent().is_virtual_sink(), - self.get_all_params(t), - )] - #check that the virtual sink's stream id is unique - if self.get_parent().is_virtual_sink(): - if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled - raise Exception, 'Stream ID "%s" is not unique.'%v - #check that the virtual source's steam id is found - if self.get_parent().is_virtual_source(): - if v not in ids: - raise Exception, 'Stream ID "%s" is not found.'%v - return v - ######################### - # GUI Position/Hint - ######################### - elif t == 'gui_hint': - if ':' in v: tab, pos = v.split(':') - elif '@' in v: tab, pos = v, '' - else: tab, pos = '', v + def evaluate(self): + """ + Evaluate the value. + + Returns: + evaluated type + """ + self._init = True + self._lisitify_flag = False + self._stringify_flag = False + self._hostage_cells = list() + def eval_string(v): + try: + e = self.get_parent().get_parent().evaluate(v) + if isinstance(e, str): return e + raise Exception #want to stringify + except: + self._stringify_flag = True + return v + t = self.get_type() + v = self.get_value() + ######################### + # Enum Type + ######################### + if self.is_enum(): return v + ######################### + # Numeric Types + ######################### + elif t in ('raw', 'complex', 'real', 'float', 'int', 'hex', 'bool'): + #raise exception if python cannot evaluate this value + try: e = self.get_parent().get_parent().evaluate(v) + except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) + #raise an exception if the data is invalid + if t == 'raw': return e + elif t == 'complex': + if not isinstance(e, COMPLEX_TYPES): + raise Exception, 'Expression "%s" is invalid for type complex.'%str(e) + return e + elif t == 'real' or t == 'float': + if not isinstance(e, REAL_TYPES): + raise Exception, 'Expression "%s" is invalid for type float.'%str(e) + return e + elif t == 'int': + if not isinstance(e, INT_TYPES): + raise Exception, 'Expression "%s" is invalid for type integer.'%str(e) + return e + elif t == 'hex': return hex(e) + elif t == 'bool': + if not isinstance(e, bool): + raise Exception, 'Expression "%s" is invalid for type bool.'%str(e) + return e + else: raise TypeError, 'Type "%s" not handled'%t + ######################### + # Numeric Vector Types + ######################### + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): + if not v: v = '()' #turn a blank string into an empty list, so it will eval + #raise exception if python cannot evaluate this value + try: e = self.get_parent().get_parent().evaluate(v) + except Exception, e: raise Exception, 'Value "%s" cannot be evaluated:\n%s'%(v, e) + #raise an exception if the data is invalid + if t == 'complex_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, COMPLEX_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type complex vector.'%str(e) + return e + elif t == 'real_vector' or t == 'float_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, REAL_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type float vector.'%str(e) + return e + elif t == 'int_vector': + if not isinstance(e, VECTOR_TYPES): + self._lisitify_flag = True + e = [e] + if not all([isinstance(ei, INT_TYPES) for ei in e]): + raise Exception, 'Expression "%s" is invalid for type integer vector.'%str(e) + return e + ######################### + # String Types + ######################### + elif t in ('string', 'file_open', 'file_save'): + #do not check if file/directory exists, that is a runtime issue + e = eval_string(v) + return str(e) + ######################### + # Unique ID Type + ######################### + elif t == 'id': + #can python use this as a variable? + if not _check_id_matcher.match(v): + raise Exception, 'ID "%s" must begin with a letter and may contain letters, numbers, and underscores.'%v + ids = [param.get_value() for param in self.get_all_params(t)] + if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled + raise Exception, 'ID "%s" is not unique.'%v + if v in ID_BLACKLIST: + raise Exception, 'ID "%s" is blacklisted.'%v + return v + ######################### + # Stream ID Type + ######################### + elif t == 'stream_id': + #get a list of all stream ids used in the virtual sinks + ids = [param.get_value() for param in filter( + lambda p: p.get_parent().is_virtual_sink(), + self.get_all_params(t), + )] + #check that the virtual sink's stream id is unique + if self.get_parent().is_virtual_sink(): + if ids.count(v) > 1: #id should only appear once, or zero times if block is disabled + raise Exception, 'Stream ID "%s" is not unique.'%v + #check that the virtual source's steam id is found + if self.get_parent().is_virtual_source(): + if v not in ids: + raise Exception, 'Stream ID "%s" is not found.'%v + return v + ######################### + # GUI Position/Hint + ######################### + elif t == 'gui_hint': + if ':' in v: tab, pos = v.split(':') + elif '@' in v: tab, pos = v, '' + else: tab, pos = '', v - if '@' in tab: tab, index = tab.split('@') - else: index = '?' + if '@' in tab: tab, index = tab.split('@') + else: index = '?' - widget_str = ({ - (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', - (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', - (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', - (False, False): 'self.top_layout.addWidget(%(widget)s)', - }[bool(tab), bool(pos)])%{'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} + widget_str = ({ + (True, True): 'self.%(tab)s_grid_layout_%(index)s.addWidget(%(widget)s, %(pos)s)', + (True, False): 'self.%(tab)s_layout_%(index)s.addWidget(%(widget)s)', + (False, True): 'self.top_grid_layout.addWidget(%(widget)s, %(pos)s)', + (False, False): 'self.top_layout.addWidget(%(widget)s)', + }[bool(tab), bool(pos)])%{'tab': tab, 'index': index, 'widget': '%s', 'pos': pos} - def gui_hint(ws, w): - if 'layout' in w: ws = ws.replace('addWidget', 'addLayout') - return ws%w + def gui_hint(ws, w): + if 'layout' in w: ws = ws.replace('addWidget', 'addLayout') + return ws%w - return lambda w: gui_hint(widget_str, w) - ######################### - # Grid Position Type - ######################### - elif t == 'grid_pos': - if not v: return '' #allow for empty grid pos - e = self.get_parent().get_parent().evaluate(v) - if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): - raise Exception, 'A grid position must be a list of 4 integers.' - row, col, row_span, col_span = e - #check row, col - if row < 0 or col < 0: - raise Exception, 'Row and column must be non-negative.' - #check row span, col span - if row_span <= 0 or col_span <= 0: - raise Exception, 'Row and column span must be greater than zero.' - #get hostage cell parent - try: my_parent = self.get_parent().get_param('notebook').evaluate() - except: my_parent = '' - #calculate hostage cells - for r in range(row_span): - for c in range(col_span): - self._hostage_cells.append((my_parent, (row+r, col+c))) - #avoid collisions - params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) - for param in params: - for parent, cell in param._hostage_cells: - if (parent, cell) in self._hostage_cells: - raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell)) - return e - ######################### - # Notebook Page Type - ######################### - elif t == 'notebook': - if not v: return '' #allow for empty notebook - #get a list of all notebooks - notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) - #check for notebook param syntax - try: notebook_id, page_index = map(str.strip, v.split(',')) - except: raise Exception, 'Bad notebook page format.' - #check that the notebook id is valid - try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] - except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id - #check that page index exists - if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): - raise Exception, 'Page index "%s" is not a valid index number.'%page_index - return notebook_id, page_index - ######################### - # Import Type - ######################### - elif t == 'import': - n = dict() #new namespace - try: exec v in n - except ImportError: raise Exception, 'Import "%s" failed.'%v - except Exception: raise Exception, 'Bad import syntax: "%s".'%v - return filter(lambda k: str(k) != '__builtins__', n.keys()) - ######################### - else: raise TypeError, 'Type "%s" not handled'%t + return lambda w: gui_hint(widget_str, w) + ######################### + # Grid Position Type + ######################### + elif t == 'grid_pos': + if not v: return '' #allow for empty grid pos + e = self.get_parent().get_parent().evaluate(v) + if not isinstance(e, (list, tuple)) or len(e) != 4 or not all([isinstance(ei, int) for ei in e]): + raise Exception, 'A grid position must be a list of 4 integers.' + row, col, row_span, col_span = e + #check row, col + if row < 0 or col < 0: + raise Exception, 'Row and column must be non-negative.' + #check row span, col span + if row_span <= 0 or col_span <= 0: + raise Exception, 'Row and column span must be greater than zero.' + #get hostage cell parent + try: my_parent = self.get_parent().get_param('notebook').evaluate() + except: my_parent = '' + #calculate hostage cells + for r in range(row_span): + for c in range(col_span): + self._hostage_cells.append((my_parent, (row+r, col+c))) + #avoid collisions + params = filter(lambda p: p is not self, self.get_all_params('grid_pos')) + for param in params: + for parent, cell in param._hostage_cells: + if (parent, cell) in self._hostage_cells: + raise Exception, 'Another graphical element is using parent "%s", cell "%s".'%(str(parent), str(cell)) + return e + ######################### + # Notebook Page Type + ######################### + elif t == 'notebook': + if not v: return '' #allow for empty notebook + #get a list of all notebooks + notebook_blocks = filter(lambda b: b.get_key() == 'notebook', self.get_parent().get_parent().get_enabled_blocks()) + #check for notebook param syntax + try: notebook_id, page_index = map(str.strip, v.split(',')) + except: raise Exception, 'Bad notebook page format.' + #check that the notebook id is valid + try: notebook_block = filter(lambda b: b.get_id() == notebook_id, notebook_blocks)[0] + except: raise Exception, 'Notebook id "%s" is not an existing notebook id.'%notebook_id + #check that page index exists + if int(page_index) not in range(len(notebook_block.get_param('labels').evaluate())): + raise Exception, 'Page index "%s" is not a valid index number.'%page_index + return notebook_id, page_index + ######################### + # Import Type + ######################### + elif t == 'import': + n = dict() #new namespace + try: exec v in n + except ImportError: raise Exception, 'Import "%s" failed.'%v + except Exception: raise Exception, 'Bad import syntax: "%s".'%v + return filter(lambda k: str(k) != '__builtins__', n.keys()) + ######################### + else: raise TypeError, 'Type "%s" not handled'%t - def to_code(self): - """ - Convert the value to code. - For string and list types, check the init flag, call evaluate(). - This ensures that evaluate() was called to set the xxxify_flags. - - Returns: - a string representing the code - """ - v = self.get_value() - t = self.get_type() - if t in ('string', 'file_open', 'file_save'): #string types - if not self._init: self.evaluate() - if self._stringify_flag: return '"%s"'%v.replace('"', '\"') - else: return v - elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types - if not self._init: self.evaluate() - if self._lisitify_flag: return '(%s, )'%v - else: return '(%s)'%v - else: return v + def to_code(self): + """ + Convert the value to code. + For string and list types, check the init flag, call evaluate(). + This ensures that evaluate() was called to set the xxxify_flags. + + Returns: + a string representing the code + """ + v = self.get_value() + t = self.get_type() + if t in ('string', 'file_open', 'file_save'): #string types + if not self._init: self.evaluate() + if self._stringify_flag: return '"%s"'%v.replace('"', '\"') + else: return v + elif t in ('complex_vector', 'real_vector', 'float_vector', 'int_vector'): #vector types + if not self._init: self.evaluate() + if self._lisitify_flag: return '(%s, )'%v + else: return '(%s)'%v + else: return v - def get_all_params(self, type): - """ - Get all the params from the flowgraph that have the given type. - - Args: - type: the specified type - - Returns: - a list of params - """ - return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) + def get_all_params(self, type): + """ + Get all the params from the flowgraph that have the given type. + + Args: + type: the specified type + + Returns: + a list of params + """ + return sum([filter(lambda p: p.get_type() == type, block.get_params()) for block in self.get_parent().get_parent().get_enabled_blocks()], []) diff --git a/grc/python/Platform.py b/grc/python/Platform.py index e036361ff0..f6adaf47a5 100644 --- a/grc/python/Platform.py +++ b/grc/python/Platform.py @@ -28,43 +28,43 @@ from Port import Port as _Port from Param import Param as _Param from Generator import Generator from Constants import \ - HIER_BLOCKS_LIB_DIR, BLOCK_DTD, \ - DEFAULT_FLOW_GRAPH, BLOCKS_DIRS + HIER_BLOCKS_LIB_DIR, BLOCK_DTD, \ + DEFAULT_FLOW_GRAPH, BLOCKS_DIRS import Constants COLORS = [(name, color) for name, key, sizeof, color in Constants.CORE_TYPES] class Platform(_Platform, _GUIPlatform): - def __init__(self): - """ - Make a platform for gnuradio. - """ - #ensure hier dir - if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR) - #convert block paths to absolute paths - block_paths = set(map(os.path.abspath, BLOCKS_DIRS)) - #init - _Platform.__init__( - self, - name='GNU Radio Companion', - version=gr.version(), - key='grc', - license=__doc__.strip(), - website='http://gnuradio.org/redmine/wiki/gnuradio/GNURadioCompanion', - block_paths=block_paths, - block_dtd=BLOCK_DTD, - default_flow_graph=DEFAULT_FLOW_GRAPH, - generator=Generator, - colors=COLORS, - ) - _GUIPlatform.__init__(self) + def __init__(self): + """ + Make a platform for gnuradio. + """ + #ensure hier dir + if not os.path.exists(HIER_BLOCKS_LIB_DIR): os.mkdir(HIER_BLOCKS_LIB_DIR) + #convert block paths to absolute paths + block_paths = set(map(os.path.abspath, BLOCKS_DIRS)) + #init + _Platform.__init__( + self, + name='GNU Radio Companion', + version=gr.version(), + key='grc', + license=__doc__.strip(), + website='http://gnuradio.org/redmine/wiki/gnuradio/GNURadioCompanion', + block_paths=block_paths, + block_dtd=BLOCK_DTD, + default_flow_graph=DEFAULT_FLOW_GRAPH, + generator=Generator, + colors=COLORS, + ) + _GUIPlatform.__init__(self) - ############################################## - # Constructors - ############################################## - FlowGraph = _FlowGraph - Connection = _Connection - Block = _Block - Port = _Port - Param = _Param + ############################################## + # Constructors + ############################################## + FlowGraph = _FlowGraph + Connection = _Connection + Block = _Block + Port = _Port + Param = _Param diff --git a/grc/python/Port.py b/grc/python/Port.py index d4afa6cf77..247dbed3e6 100644 --- a/grc/python/Port.py +++ b/grc/python/Port.py @@ -22,203 +22,203 @@ from .. gui.Port import Port as _GUIPort import Constants def _get_source_from_virtual_sink_port(vsp): - """ - Resolve the source port that is connected to the given virtual sink port. - Use the get source from virtual source to recursively resolve subsequent ports. - """ - try: return _get_source_from_virtual_source_port( - vsp.get_enabled_connections()[0].get_source()) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + """ + Resolve the source port that is connected to the given virtual sink port. + Use the get source from virtual source to recursively resolve subsequent ports. + """ + try: return _get_source_from_virtual_source_port( + vsp.get_enabled_connections()[0].get_source()) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp def _get_source_from_virtual_source_port(vsp, traversed=[]): - """ - Recursively resolve source ports over the virtual connections. - Keep track of traversed sources to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_source(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp - try: return _get_source_from_virtual_source_port( - _get_source_from_virtual_sink_port( - filter(#get all virtual sinks with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_sink(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sinks()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + """ + Recursively resolve source ports over the virtual connections. + Keep track of traversed sources to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_source(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual source %s'%vsp + try: return _get_source_from_virtual_source_port( + _get_source_from_virtual_sink_port( + filter(#get all virtual sinks with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter(#get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_sink(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sinks()[0] + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp def _get_sink_from_virtual_source_port(vsp): - """ - Resolve the sink port that is connected to the given virtual source port. - Use the get sink from virtual sink to recursively resolve subsequent ports. - """ - try: return _get_sink_from_virtual_sink_port( - vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first - except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp + """ + Resolve the sink port that is connected to the given virtual source port. + Use the get sink from virtual sink to recursively resolve subsequent ports. + """ + try: return _get_sink_from_virtual_sink_port( + vsp.get_enabled_connections()[0].get_sink()) # Could have many connections, but use first + except: raise Exception, 'Could not resolve source for virtual source port %s'%vsp def _get_sink_from_virtual_sink_port(vsp, traversed=[]): - """ - Recursively resolve sink ports over the virtual connections. - Keep track of traversed sinks to avoid recursive loops. - """ - if not vsp.get_parent().is_virtual_sink(): return vsp - if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp - try: return _get_sink_from_virtual_sink_port( - _get_sink_from_virtual_source_port( - filter(#get all virtual source with a matching stream id - lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), - filter(#get all enabled blocks that are also virtual sinks - lambda b: b.is_virtual_source(), - vsp.get_parent().get_parent().get_enabled_blocks(), - ), - )[0].get_sources()[0] - ), traversed + [vsp], - ) - except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp + """ + Recursively resolve sink ports over the virtual connections. + Keep track of traversed sinks to avoid recursive loops. + """ + if not vsp.get_parent().is_virtual_sink(): return vsp + if vsp in traversed: raise Exception, 'Loop found when resolving virtual sink %s'%vsp + try: return _get_sink_from_virtual_sink_port( + _get_sink_from_virtual_source_port( + filter(#get all virtual source with a matching stream id + lambda vs: vs.get_param('stream_id').get_value() == vsp.get_parent().get_param('stream_id').get_value(), + filter(#get all enabled blocks that are also virtual sinks + lambda b: b.is_virtual_source(), + vsp.get_parent().get_parent().get_enabled_blocks(), + ), + )[0].get_sources()[0] + ), traversed + [vsp], + ) + except: raise Exception, 'Could not resolve source for virtual sink port %s'%vsp class Port(_Port, _GUIPort): - def __init__(self, block, n, dir): - """ - Make a new port from nested data. - - Args: - block: the parent element - n: the nested odict - dir: the direction - """ - self._n = n - if n['type'] == 'msg': n['key'] = 'msg' - if n['type'] == 'message': n['key'] = n['name'] - if dir == 'source' and not n.find('key'): - n['key'] = str(block._source_count) - block._source_count += 1 - if dir == 'sink' and not n.find('key'): - n['key'] = str(block._sink_count) - block._sink_count += 1 - #build the port - _Port.__init__( - self, - block=block, - n=n, - dir=dir, - ) - _GUIPort.__init__(self) - self._nports = n.find('nports') or '' - self._vlen = n.find('vlen') or '' - self._optional = bool(n.find('optional')) - - def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() - - def is_type_empty(self): return not self._n['type'] - - def validate(self): - _Port.validate(self) - if not self.get_enabled_connections() and not self.get_optional(): - self.add_error_message('Port is not connected.') - if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: - self.add_error_message('Port has too many connections.') - #message port logic - if self.get_type() == 'msg': - if self.get_nports(): - self.add_error_message('A port of type "msg" cannot have "nports" set.') - if self.get_vlen() != 1: - self.add_error_message('A port of type "msg" must have a "vlen" of 1.') - - def rewrite(self): - """ - Handle the port cloning for virtual blocks. - """ - _Port.rewrite(self) - if self.is_type_empty(): - try: #clone type and vlen - source = self.resolve_empty_type() - self._type = str(source.get_type()) - self._vlen = str(source.get_vlen()) - except: #reset type and vlen - self._type = '' - self._vlen = '' - - def resolve_virtual_source(self): - if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) - if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) - - def resolve_empty_type(self): - if self.is_sink(): - try: - src = _get_source_from_virtual_sink_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_sink_port(self) - if not sink.is_type_empty(): return sink - if self.is_source(): - try: - src = _get_source_from_virtual_source_port(self) - if not src.is_type_empty(): return src - except: pass - sink = _get_sink_from_virtual_source_port(self) - if not sink.is_type_empty(): return sink - - def get_vlen(self): - """ - Get the vector length. - If the evaluation of vlen cannot be cast to an integer, return 1. - - Returns: - the vector length or 1 - """ - vlen = self.get_parent().resolve_dependencies(self._vlen) - try: return int(self.get_parent().get_parent().evaluate(vlen)) - except: return 1 - - def get_nports(self): - """ - Get the number of ports. - If already blank, return a blank - If the evaluation of nports cannot be cast to an integer, return 1. - - Returns: - the number of ports or 1 - """ - nports = self.get_parent().resolve_dependencies(self._nports) - #return blank if nports is blank - if not nports: return '' - try: - nports = int(self.get_parent().get_parent().evaluate(nports)) - if 0 < nports: return nports - except: return 1 - - def get_optional(self): return bool(self._optional) - - def get_color(self): - """ - Get the color that represents this port's type. - Codes differ for ports where the vec length is 1 or greater than 1. - - Returns: - a hex color code. - """ - try: - color = Constants.TYPE_TO_COLOR[self.get_type()] - vlen = self.get_vlen() - if vlen == 1: return color - color_val = int(color[1:], 16) - r = (color_val >> 16) & 0xff - g = (color_val >> 8) & 0xff - b = (color_val >> 0) & 0xff - dark = (0, 0, 30, 50, 70)[min(4, vlen)] - r = max(r-dark, 0) - g = max(g-dark, 0) - b = max(b-dark, 0) - return '#%.2x%.2x%.2x'%(r, g, b) - except: return _Port.get_color(self) - - def copy(self, new_key=None): - n = self._n.copy() - #remove nports from the key so the copy cannot be a duplicator - if n.has_key('nports'): n.pop('nports') - if new_key: n['key'] = new_key - return self.__class__(self.get_parent(), n, self._dir) + def __init__(self, block, n, dir): + """ + Make a new port from nested data. + + Args: + block: the parent element + n: the nested odict + dir: the direction + """ + self._n = n + if n['type'] == 'msg': n['key'] = 'msg' + if n['type'] == 'message': n['key'] = n['name'] + if dir == 'source' and not n.find('key'): + n['key'] = str(block._source_count) + block._source_count += 1 + if dir == 'sink' and not n.find('key'): + n['key'] = str(block._sink_count) + block._sink_count += 1 + #build the port + _Port.__init__( + self, + block=block, + n=n, + dir=dir, + ) + _GUIPort.__init__(self) + self._nports = n.find('nports') or '' + self._vlen = n.find('vlen') or '' + self._optional = bool(n.find('optional')) + + def get_types(self): return Constants.TYPE_TO_SIZEOF.keys() + + def is_type_empty(self): return not self._n['type'] + + def validate(self): + _Port.validate(self) + if not self.get_enabled_connections() and not self.get_optional(): + self.add_error_message('Port is not connected.') + if not self.is_source() and (not self.get_type() == "message") and len(self.get_enabled_connections()) > 1: + self.add_error_message('Port has too many connections.') + #message port logic + if self.get_type() == 'msg': + if self.get_nports(): + self.add_error_message('A port of type "msg" cannot have "nports" set.') + if self.get_vlen() != 1: + self.add_error_message('A port of type "msg" must have a "vlen" of 1.') + + def rewrite(self): + """ + Handle the port cloning for virtual blocks. + """ + _Port.rewrite(self) + if self.is_type_empty(): + try: #clone type and vlen + source = self.resolve_empty_type() + self._type = str(source.get_type()) + self._vlen = str(source.get_vlen()) + except: #reset type and vlen + self._type = '' + self._vlen = '' + + def resolve_virtual_source(self): + if self.get_parent().is_virtual_sink(): return _get_source_from_virtual_sink_port(self) + if self.get_parent().is_virtual_source(): return _get_source_from_virtual_source_port(self) + + def resolve_empty_type(self): + if self.is_sink(): + try: + src = _get_source_from_virtual_sink_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_sink_port(self) + if not sink.is_type_empty(): return sink + if self.is_source(): + try: + src = _get_source_from_virtual_source_port(self) + if not src.is_type_empty(): return src + except: pass + sink = _get_sink_from_virtual_source_port(self) + if not sink.is_type_empty(): return sink + + def get_vlen(self): + """ + Get the vector length. + If the evaluation of vlen cannot be cast to an integer, return 1. + + Returns: + the vector length or 1 + """ + vlen = self.get_parent().resolve_dependencies(self._vlen) + try: return int(self.get_parent().get_parent().evaluate(vlen)) + except: return 1 + + def get_nports(self): + """ + Get the number of ports. + If already blank, return a blank + If the evaluation of nports cannot be cast to an integer, return 1. + + Returns: + the number of ports or 1 + """ + nports = self.get_parent().resolve_dependencies(self._nports) + #return blank if nports is blank + if not nports: return '' + try: + nports = int(self.get_parent().get_parent().evaluate(nports)) + if 0 < nports: return nports + except: return 1 + + def get_optional(self): return bool(self._optional) + + def get_color(self): + """ + Get the color that represents this port's type. + Codes differ for ports where the vec length is 1 or greater than 1. + + Returns: + a hex color code. + """ + try: + color = Constants.TYPE_TO_COLOR[self.get_type()] + vlen = self.get_vlen() + if vlen == 1: return color + color_val = int(color[1:], 16) + r = (color_val >> 16) & 0xff + g = (color_val >> 8) & 0xff + b = (color_val >> 0) & 0xff + dark = (0, 0, 30, 50, 70)[min(4, vlen)] + r = max(r-dark, 0) + g = max(g-dark, 0) + b = max(b-dark, 0) + return '#%.2x%.2x%.2x'%(r, g, b) + except: return _Port.get_color(self) + + def copy(self, new_key=None): + n = self._n.copy() + #remove nports from the key so the copy cannot be a duplicator + if n.has_key('nports'): n.pop('nports') + if new_key: n['key'] = new_key + return self.__class__(self.get_parent(), n, self._dir) diff --git a/grc/python/block.dtd b/grc/python/block.dtd index 292ea06cb6..21ffbe09af 100644 --- a/grc/python/block.dtd +++ b/grc/python/block.dtd @@ -17,25 +17,25 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA --> <!-- - gnuradio_python.blocks.dtd - Josh Blum - The document type definition for blocks. + gnuradio_python.blocks.dtd + Josh Blum + The document type definition for blocks. --> <!-- - Top level element. - A block contains a name, ...parameters list, and list of IO ports. + Top level element. + A block contains a name, ...parameters list, and list of IO ports. --> -<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, check*, sink*, source*, doc?, grc_source?)> +<!ELEMENT block (name, key, category?, throttle?, import*, var_make?, make, callback*, param*, bus_sink?, bus_source?, check*, sink*, source*, bus_structure_sink?, bus_structure_source?, doc?, grc_source?)> <!-- - Sub level elements. + Sub level elements. --> <!ELEMENT param (name, key, value?, type, hide?, option*)> <!ELEMENT option (name, key, opt*)> <!ELEMENT sink (name, type, vlen?, nports?, optional?)> <!ELEMENT source (name, type, vlen?, nports?, optional?)> <!-- - Bottom level elements. - Character data only. + Bottom level elements. + Character data only. --> <!ELEMENT category (#PCDATA)> <!ELEMENT import (#PCDATA)> @@ -44,11 +44,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA <!ELEMENT name (#PCDATA)> <!ELEMENT key (#PCDATA)> <!ELEMENT check (#PCDATA)> +<!ELEMENT bus_sink (#PCDATA)> +<!ELEMENT bus_source (#PCDATA)> <!ELEMENT opt (#PCDATA)> <!ELEMENT type (#PCDATA)> <!ELEMENT hide (#PCDATA)> <!ELEMENT vlen (#PCDATA)> <!ELEMENT nports (#PCDATA)> +<!ELEMENT bus_structure_sink (#PCDATA)> +<!ELEMENT bus_structure_source (#PCDATA)> <!ELEMENT var_make (#PCDATA)> <!ELEMENT make (#PCDATA)> <!ELEMENT value (#PCDATA)> diff --git a/grc/python/convert_hier.py b/grc/python/convert_hier.py index 508ec63b2b..dc86daa0ef 100644 --- a/grc/python/convert_hier.py +++ b/grc/python/convert_hier.py @@ -22,77 +22,89 @@ from .. base import ParseXML from .. base import odict def convert_hier(flow_graph, python_file): - #extract info from the flow graph - input_sigs = flow_graph.get_io_signaturev('in') - output_sigs = flow_graph.get_io_signaturev('out') - input_msgp = flow_graph.get_msg_pad_sources(); - output_msgp = flow_graph.get_msg_pad_sinks(); - parameters = flow_graph.get_parameters() - block_key = flow_graph.get_option('id') - block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() - block_category = flow_graph.get_option('category') - block_desc = flow_graph.get_option('description') - block_author = flow_graph.get_option('author') - #build the nested data - block_n = odict() - block_n['name'] = block_name - block_n['key'] = block_key - block_n['category'] = block_category - block_n['import'] = 'execfile("%s")'%python_file - #make data - if parameters: block_n['make'] = '%s(\n\t%s,\n)'%( - block_key, - ',\n\t'.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]), - ) - else: block_n['make'] = '%s()'%block_key - #callback data - block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters] - #param data - params_n = list() - for param in parameters: - param_n = odict() - param_n['name'] = param.get_param('label').get_value() or param.get_id() - param_n['key'] = param.get_id() - param_n['value'] = param.get_param('value').get_value() - param_n['type'] = 'raw' - params_n.append(param_n) - block_n['param'] = params_n - #sink data stream ports - block_n['sink'] = list() - for input_sig in input_sigs: - sink_n = odict() - sink_n['name'] = input_sig['label'] - sink_n['type'] = input_sig['type'] - sink_n['vlen'] = input_sig['vlen'] - if input_sig['optional']: sink_n['optional'] = '1' - block_n['sink'].append(sink_n) - #sink data msg ports - for input_sig in input_msgp: - sink_n = odict() - sink_n['name'] = input_sig.get_param("label").get_value(); - sink_n['type'] = "message" - sink_n['optional'] = input_sig.get_param("optional").get_value(); - block_n['sink'].append(sink_n) - #source data stream ports - block_n['source'] = list() - for output_sig in output_sigs: - source_n = odict() - source_n['name'] = output_sig['label'] - source_n['type'] = output_sig['type'] - source_n['vlen'] = output_sig['vlen'] - if output_sig['optional']: source_n['optional'] = '1' - block_n['source'].append(source_n) - #source data msg ports - for output_sig in output_msgp: - source_n = odict() - source_n['name'] = output_sig.get_param("label").get_value(); - source_n['type'] = "message" - source_n['optional'] = output_sig.get_param("optional").get_value(); - block_n['source'].append(source_n) - #doc data - block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) - block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) - #write the block_n to file - xml_file = python_file + '.xml' - ParseXML.to_file({'block': block_n}, xml_file) - ParseXML.validate_dtd(xml_file, BLOCK_DTD) + #extract info from the flow graph + input_sigs = flow_graph.get_io_signaturev('in') + output_sigs = flow_graph.get_io_signaturev('out') + input_msgp = flow_graph.get_msg_pad_sources(); + output_msgp = flow_graph.get_msg_pad_sinks(); + parameters = flow_graph.get_parameters() + bussink = flow_graph.get_bussink() + bussrc = flow_graph.get_bussrc() + bus_struct_sink = flow_graph.get_bus_structure_sink() + bus_struct_src = flow_graph.get_bus_structure_src() + block_key = flow_graph.get_option('id') + block_name = flow_graph.get_option('title') or flow_graph.get_option('id').replace('_', ' ').title() + block_category = flow_graph.get_option('category') + block_desc = flow_graph.get_option('description') + block_author = flow_graph.get_option('author') + #build the nested data + block_n = odict() + block_n['name'] = block_name + block_n['key'] = block_key + block_n['category'] = block_category + block_n['import'] = 'execfile("%s")'%python_file + #make data + if parameters: block_n['make'] = '%s(\n %s,\n)'%( + block_key, + ',\n '.join(['%s=$%s'%(param.get_id(), param.get_id()) for param in parameters]), + ) + else: block_n['make'] = '%s()'%block_key + #callback data + block_n['callback'] = ['set_%s($%s)'%(param.get_id(), param.get_id()) for param in parameters] + #param data + params_n = list() + for param in parameters: + param_n = odict() + param_n['name'] = param.get_param('label').get_value() or param.get_id() + param_n['key'] = param.get_id() + param_n['value'] = param.get_param('value').get_value() + param_n['type'] = 'raw' + params_n.append(param_n) + block_n['param'] = params_n + #sink data stream ports + if bussink: + block_n['bus_sink'] = '1'; + if bussrc: + block_n['bus_source'] = '1'; + block_n['sink'] = list() + for input_sig in input_sigs: + sink_n = odict() + sink_n['name'] = input_sig['label'] + sink_n['type'] = input_sig['type'] + sink_n['vlen'] = input_sig['vlen'] + if input_sig['optional']: sink_n['optional'] = '1' + block_n['sink'].append(sink_n) + #sink data msg ports + for input_sig in input_msgp: + sink_n = odict() + sink_n['name'] = input_sig.get_param("label").get_value(); + sink_n['type'] = "message" + sink_n['optional'] = input_sig.get_param("optional").get_value(); + block_n['sink'].append(sink_n) + #source data stream ports + block_n['source'] = list() + if bus_struct_sink: + block_n['bus_structure_sink'] = bus_struct_sink[0].get_param('struct').get_value(); + if bus_struct_src: + block_n['bus_structure_source'] = bus_struct_src[0].get_param('struct').get_value(); + for output_sig in output_sigs: + source_n = odict() + source_n['name'] = output_sig['label'] + source_n['type'] = output_sig['type'] + source_n['vlen'] = output_sig['vlen'] + if output_sig['optional']: source_n['optional'] = '1' + block_n['source'].append(source_n) + #source data msg ports + for output_sig in output_msgp: + source_n = odict() + source_n['name'] = output_sig.get_param("label").get_value(); + source_n['type'] = "message" + source_n['optional'] = output_sig.get_param("optional").get_value(); + block_n['source'].append(source_n) + #doc data + block_n['doc'] = "%s\n%s\n%s"%(block_author, block_desc, python_file) + block_n['grc_source'] = "%s"%(flow_graph.grc_file_path) + #write the block_n to file + xml_file = python_file + '.xml' + ParseXML.to_file({'block': block_n}, xml_file) + ParseXML.validate_dtd(xml_file, BLOCK_DTD) diff --git a/grc/python/default_flow_graph.grc b/grc/python/default_flow_graph.grc index dea26f3a5e..53d39e885a 100644 --- a/grc/python/default_flow_graph.grc +++ b/grc/python/default_flow_graph.grc @@ -2,42 +2,42 @@ <!-- ################################################### ##Default Flow Graph: -## include an options block and a variable for sample rate +## include an options block and a variable for sample rate ################################################### --> <flow_graph> - <block> - <key>options</key> - <param> - <key>id</key> - <value>top_block</value> - </param> - <param> - <key>_coordinate</key> - <value>(10, 10)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> - <block> - <key>variable</key> - <param> - <key>id</key> - <value>samp_rate</value> - </param> - <param> - <key>value</key> - <value>32000</value> - </param> - <param> - <key>_coordinate</key> - <value>(10, 170)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - </block> + <block> + <key>options</key> + <param> + <key>id</key> + <value>top_block</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 10)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> + <block> + <key>variable</key> + <param> + <key>id</key> + <value>samp_rate</value> + </param> + <param> + <key>value</key> + <value>32000</value> + </param> + <param> + <key>_coordinate</key> + <value>(10, 170)</value> + </param> + <param> + <key>_rotation</key> + <value>0</value> + </param> + </block> </flow_graph> diff --git a/grc/python/expr_utils.py b/grc/python/expr_utils.py index 67580f6ffc..85f420f04c 100644 --- a/grc/python/expr_utils.py +++ b/grc/python/expr_utils.py @@ -21,157 +21,157 @@ import string VAR_CHARS = string.letters + string.digits + '_' class graph(object): - """ - Simple graph structure held in a dictionary. - """ + """ + Simple graph structure held in a dictionary. + """ - def __init__(self): self._graph = dict() + def __init__(self): self._graph = dict() - def __str__(self): return str(self._graph) + def __str__(self): return str(self._graph) - def add_node(self, node_key): - if self._graph.has_key(node_key): return - self._graph[node_key] = set() + def add_node(self, node_key): + if self._graph.has_key(node_key): return + self._graph[node_key] = set() - def remove_node(self, node_key): - if not self._graph.has_key(node_key): return - for edges in self._graph.values(): - if node_key in edges: edges.remove(node_key) - self._graph.pop(node_key) + def remove_node(self, node_key): + if not self._graph.has_key(node_key): return + for edges in self._graph.values(): + if node_key in edges: edges.remove(node_key) + self._graph.pop(node_key) - def add_edge(self, src_node_key, dest_node_key): - self._graph[src_node_key].add(dest_node_key) + def add_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].add(dest_node_key) - def remove_edge(self, src_node_key, dest_node_key): - self._graph[src_node_key].remove(dest_node_key) + def remove_edge(self, src_node_key, dest_node_key): + self._graph[src_node_key].remove(dest_node_key) - def get_nodes(self): return self._graph.keys() + def get_nodes(self): return self._graph.keys() - def get_edges(self, node_key): return self._graph[node_key] + def get_edges(self, node_key): return self._graph[node_key] def expr_split(expr): - """ - Split up an expression by non alphanumeric characters, including underscore. - Leave strings in-tact. - #TODO ignore escaped quotes, use raw strings. - - Args: - expr: an expression string - - Returns: - a list of string tokens that form expr - """ - toks = list() - tok = '' - quote = '' - for char in expr: - if quote or char in VAR_CHARS: - if char == quote: quote = '' - tok += char - elif char in ("'", '"'): - toks.append(tok) - tok = char - quote = char - else: - toks.append(tok) - toks.append(char) - tok = '' - toks.append(tok) - return filter(lambda t: t, toks) + """ + Split up an expression by non alphanumeric characters, including underscore. + Leave strings in-tact. + #TODO ignore escaped quotes, use raw strings. + + Args: + expr: an expression string + + Returns: + a list of string tokens that form expr + """ + toks = list() + tok = '' + quote = '' + for char in expr: + if quote or char in VAR_CHARS: + if char == quote: quote = '' + tok += char + elif char in ("'", '"'): + toks.append(tok) + tok = char + quote = char + else: + toks.append(tok) + toks.append(char) + tok = '' + toks.append(tok) + return filter(lambda t: t, toks) def expr_replace(expr, replace_dict): - """ - Search for vars in the expression and add the prepend. - - Args: - expr: an expression string - replace_dict: a dict of find:replace - - Returns: - a new expression with the prepend - """ - expr_splits = expr_split(expr) - for i, es in enumerate(expr_splits): - if es in replace_dict.keys(): - expr_splits[i] = replace_dict[es] - return ''.join(expr_splits) + """ + Search for vars in the expression and add the prepend. + + Args: + expr: an expression string + replace_dict: a dict of find:replace + + Returns: + a new expression with the prepend + """ + expr_splits = expr_split(expr) + for i, es in enumerate(expr_splits): + if es in replace_dict.keys(): + expr_splits[i] = replace_dict[es] + return ''.join(expr_splits) def get_variable_dependencies(expr, vars): - """ - Return a set of variables used in this expression. - - Args: - expr: an expression string - vars: a list of variable names - - Returns: - a subset of vars used in the expression - """ - expr_toks = expr_split(expr) - return set(filter(lambda v: v in expr_toks, vars)) + """ + Return a set of variables used in this expression. + + Args: + expr: an expression string + vars: a list of variable names + + Returns: + a subset of vars used in the expression + """ + expr_toks = expr_split(expr) + return set(filter(lambda v: v in expr_toks, vars)) def get_graph(exprs): - """ - Get a graph representing the variable dependencies - - Args: - exprs: a mapping of variable name to expression - - Returns: - a graph of variable deps - """ - vars = exprs.keys() - #get dependencies for each expression, load into graph - var_graph = graph() - for var in vars: var_graph.add_node(var) - for var, expr in exprs.iteritems(): - for dep in get_variable_dependencies(expr, vars): - if dep != var: var_graph.add_edge(dep, var) - return var_graph + """ + Get a graph representing the variable dependencies + + Args: + exprs: a mapping of variable name to expression + + Returns: + a graph of variable deps + """ + vars = exprs.keys() + #get dependencies for each expression, load into graph + var_graph = graph() + for var in vars: var_graph.add_node(var) + for var, expr in exprs.iteritems(): + for dep in get_variable_dependencies(expr, vars): + if dep != var: var_graph.add_edge(dep, var) + return var_graph def sort_variables(exprs): - """ - Get a list of variables in order of dependencies. - - Args: - exprs: a mapping of variable name to expression - - Returns: - a list of variable names - @throws Exception circular dependencies - """ - var_graph = get_graph(exprs) - sorted_vars = list() - #determine dependency order - while var_graph.get_nodes(): - #get a list of nodes with no edges - indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) - if not indep_vars: raise Exception('circular dependency caught in sort_variables') - #add the indep vars to the end of the list - sorted_vars.extend(sorted(indep_vars)) - #remove each edge-less node from the graph - for var in indep_vars: var_graph.remove_node(var) - return reversed(sorted_vars) + """ + Get a list of variables in order of dependencies. + + Args: + exprs: a mapping of variable name to expression + + Returns: + a list of variable names + @throws Exception circular dependencies + """ + var_graph = get_graph(exprs) + sorted_vars = list() + #determine dependency order + while var_graph.get_nodes(): + #get a list of nodes with no edges + indep_vars = filter(lambda var: not var_graph.get_edges(var), var_graph.get_nodes()) + if not indep_vars: raise Exception('circular dependency caught in sort_variables') + #add the indep vars to the end of the list + sorted_vars.extend(sorted(indep_vars)) + #remove each edge-less node from the graph + for var in indep_vars: var_graph.remove_node(var) + return reversed(sorted_vars) def sort_objects(objects, get_id, get_expr): - """ - Sort a list of objects according to their expressions. - - Args: - objects: the list of objects to sort - get_id: the function to extract an id from the object - get_expr: the function to extract an expression from the object - - Returns: - a list of sorted objects - """ - id2obj = dict([(get_id(obj), obj) for obj in objects]) - #map obj id to expression code - id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) - #sort according to dependency - sorted_ids = sort_variables(id2expr) - #return list of sorted objects - return [id2obj[id] for id in sorted_ids] + """ + Sort a list of objects according to their expressions. + + Args: + objects: the list of objects to sort + get_id: the function to extract an id from the object + get_expr: the function to extract an expression from the object + + Returns: + a list of sorted objects + """ + id2obj = dict([(get_id(obj), obj) for obj in objects]) + #map obj id to expression code + id2expr = dict([(get_id(obj), get_expr(obj)) for obj in objects]) + #sort according to dependency + sorted_ids = sort_variables(id2expr) + #return list of sorted objects + return [id2obj[id] for id in sorted_ids] if __name__ == '__main__': - for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i + for i in sort_variables({'x':'1', 'y':'x+1', 'a':'x+y', 'b':'y+1', 'c':'a+b+x+y'}): print i diff --git a/grc/python/extract_docs.py b/grc/python/extract_docs.py index 1d1c738dcc..b3b87e64ca 100644 --- a/grc/python/extract_docs.py +++ b/grc/python/extract_docs.py @@ -20,55 +20,55 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA import re def _extract(key): - """ - Extract the documentation from the python __doc__ strings. - If multiple modules match, combine the docs. - - Args: - key: the block key - - Returns: - a string with documentation - """ - #extract matches - try: - module_name, constructor_name = key.split('_', 1) - module = __import__('gnuradio.'+module_name) - module = getattr(module, module_name) - except ImportError: - try: - module_name, constructor_name = key.split('_', 1) - module = __import__(module_name) - except: return '' - except: - return '' - pattern = constructor_name.replace('_', '_*').replace('x', '\w') - pattern_matcher = re.compile('^%s\w*$'%pattern) - matches = filter(lambda x: pattern_matcher.match(x), dir(module)) - #combine all matches - doc_strs = list() - for match in matches: - try: - title = ' --- ' + match + ' --- ' - doc_strs.append('\n\n'.join([title, getattr(module, match).__doc__]).strip()) - except: pass - return '\n\n'.join(doc_strs) + """ + Extract the documentation from the python __doc__ strings. + If multiple modules match, combine the docs. + + Args: + key: the block key + + Returns: + a string with documentation + """ + #extract matches + try: + module_name, constructor_name = key.split('_', 1) + module = __import__('gnuradio.'+module_name) + module = getattr(module, module_name) + except ImportError: + try: + module_name, constructor_name = key.split('_', 1) + module = __import__(module_name) + except: return '' + except: + return '' + pattern = constructor_name.replace('_', '_*').replace('x', '\w') + pattern_matcher = re.compile('^%s\w*$'%pattern) + matches = filter(lambda x: pattern_matcher.match(x), dir(module)) + #combine all matches + doc_strs = list() + for match in matches: + try: + title = ' --- ' + match + ' --- ' + doc_strs.append('\n\n'.join([title, getattr(module, match).__doc__]).strip()) + except: pass + return '\n\n'.join(doc_strs) _docs_cache = dict() def extract(key): - """ - Call the private extract and cache the result. - - Args: - key: the block key - - Returns: - a string with documentation - """ - if not _docs_cache.has_key(key): - _docs_cache[key] = _extract(key) - return _docs_cache[key] + """ + Call the private extract and cache the result. + + Args: + key: the block key + + Returns: + a string with documentation + """ + if not _docs_cache.has_key(key): + _docs_cache[key] = _extract(key) + return _docs_cache[key] if __name__ == '__main__': - import sys - print extract(sys.argv[1]) + import sys + print extract(sys.argv[1]) diff --git a/grc/python/flow_graph.tmpl b/grc/python/flow_graph.tmpl index d104d4913d..cf789233ec 100644 --- a/grc/python/flow_graph.tmpl +++ b/grc/python/flow_graph.tmpl @@ -13,7 +13,7 @@ ##@param var_id2cbs variable id map to callback strings ######################################################## #def indent($code) -#set $code = '\n\t\t'.join(str($code).splitlines()) +#set $code = '\n '.join(str($code).splitlines()) $code#slurp #end def #import time @@ -39,304 +39,310 @@ $imp ######################################################## ##Create Class -## Write the class declaration for a top or hier block. -## The parameter names are the arguments to __init__. -## Determine the absolute icon path (wx gui only). -## Setup the IO signature (hier block only). +## Write the class declaration for a top or hier block. +## The parameter names are the arguments to __init__. +## Determine the absolute icon path (wx gui only). +## Setup the IO signature (hier block only). ######################################################## #set $class_name = $flow_graph.get_option('id') #set $param_str = ', '.join(['self'] + ['%s=%s'%(param.get_id(), param.get_make()) for param in $parameters]) #if $generate_options == 'wx_gui' - #import gtk - #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) + #import gtk + #set $icon = gtk.IconTheme().lookup_icon('gnuradio-grc', 32, 0) class $(class_name)(grc_wxgui.top_block_gui): - def __init__($param_str): - grc_wxgui.top_block_gui.__init__(self, title="$title") - #if $icon - _icon_path = "$icon.get_filename()" - self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) - #end if + def __init__($param_str): + grc_wxgui.top_block_gui.__init__(self, title="$title") + #if $icon + _icon_path = "$icon.get_filename()" + self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) + #end if #elif $generate_options == 'qt_gui' class $(class_name)(gr.top_block, Qt.QWidget): - def __init__($param_str): - gr.top_block.__init__(self, "$title") - Qt.QWidget.__init__(self) - self.setWindowTitle("$title") - try: - self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) - except: - pass - self.top_scroll_layout = Qt.QVBoxLayout() - self.setLayout(self.top_scroll_layout) - self.top_scroll = Qt.QScrollArea() - self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) - self.top_scroll_layout.addWidget(self.top_scroll) - self.top_scroll.setWidgetResizable(True) - self.top_widget = Qt.QWidget() - self.top_scroll.setWidget(self.top_widget) - self.top_layout = Qt.QVBoxLayout(self.top_widget) - self.top_grid_layout = Qt.QGridLayout() - self.top_layout.addLayout(self.top_grid_layout) + def __init__($param_str): + gr.top_block.__init__(self, "$title") + Qt.QWidget.__init__(self) + self.setWindowTitle("$title") + try: + self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) + except: + pass + self.top_scroll_layout = Qt.QVBoxLayout() + self.setLayout(self.top_scroll_layout) + self.top_scroll = Qt.QScrollArea() + self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) + self.top_scroll_layout.addWidget(self.top_scroll) + self.top_scroll.setWidgetResizable(True) + self.top_widget = Qt.QWidget() + self.top_scroll.setWidget(self.top_widget) + self.top_layout = Qt.QVBoxLayout(self.top_widget) + self.top_grid_layout = Qt.QGridLayout() + self.top_layout.addLayout(self.top_grid_layout) - self.settings = Qt.QSettings("GNU Radio", "$class_name") - self.restoreGeometry(self.settings.value("geometry").toByteArray()) + self.settings = Qt.QSettings("GNU Radio", "$class_name") + self.restoreGeometry(self.settings.value("geometry").toByteArray()) #elif $generate_options == 'no_gui' class $(class_name)(gr.top_block): - def __init__($param_str): - gr.top_block.__init__(self, "$title") + def __init__($param_str): + gr.top_block.__init__(self, "$title") #elif $generate_options == 'hb' - #set $in_sigs = $flow_graph.get_io_signaturev('in') - #set $out_sigs = $flow_graph.get_io_signaturev('out') + #set $in_sigs = $flow_graph.get_io_signaturev('in') + #set $out_sigs = $flow_graph.get_io_signaturev('out') class $(class_name)(gr.hier_block2): #def make_io_sig($io_sigs) - #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs] - #if len($io_sigs) == 0 + #set $size_strs = ['%s*%s'%(io_sig['size'], io_sig['vlen']) for io_sig in $io_sigs] + #if len($io_sigs) == 0 gr.io_signature(0, 0, 0)#slurp - #elif len($io_sigs) == 1 + #elif len($io_sigs) == 1 gr.io_signature(1, 1, $size_strs[0])#slurp - #else + #else gr.io_signaturev($(len($io_sigs)), $(len($io_sigs)), [$(', '.join($size_strs))])#slurp - #end if + #end if #end def - def __init__($param_str): - gr.hier_block2.__init__( - self, "$title", - $make_io_sig($in_sigs), - $make_io_sig($out_sigs), - ) + def __init__($param_str): + gr.hier_block2.__init__( + self, "$title", + $make_io_sig($in_sigs), + $make_io_sig($out_sigs), + ) #end if ######################################################## ##Create Parameters -## Set the parameter to a property of self. +## Set the parameter to a property of self. ######################################################## #if $parameters - $DIVIDER - # Parameters - $DIVIDER + $DIVIDER + # Parameters + $DIVIDER #end if #for $param in $parameters - $indent($param.get_var_make()) + $indent($param.get_var_make()) #end for ######################################################## ##Create Variables ######################################################## #if $variables - $DIVIDER - # Variables - $DIVIDER + $DIVIDER + # Variables + $DIVIDER #end if #for $var in $variables - $indent($var.get_var_make()) + $indent($var.get_var_make()) #end for ######################################################## ##Create Message Queues ######################################################## #if $messages - $DIVIDER - # Message Queues - $DIVIDER + $DIVIDER + # Message Queues + $DIVIDER #end if #for $msg in $messages - $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) + $(msg.get_source().get_parent().get_id())_msgq_out = $(msg.get_sink().get_parent().get_id())_msgq_in = gr.msg_queue(2) #end for ######################################################## ##Create Blocks ######################################################## #if $blocks - $DIVIDER - # Blocks - $DIVIDER + $DIVIDER + # Blocks + $DIVIDER #end if #for $blk in filter(lambda b: b.get_make(), $blocks) - #if $blk in $variables - $indent($blk.get_make()) - #else - self.$blk.get_id() = $indent($blk.get_make()) - #end if + #if $blk in $variables + $indent($blk.get_make()) + #else + self.$blk.get_id() = $indent($blk.get_make()) + #if $blk.has_param('affinity') and $blk.get_param('affinity').get_value() + (self.$blk.get_id()).set_processor_affinity($blk.get_param('affinity').get_value()) + #end if + #if (len($blk.get_sources())>0) and $blk.has_param('affinity') and (int($blk.get_param('minoutbuf').get_value()) > 0) + (self.$blk.get_id()).set_min_output_buffer($blk.get_param('minoutbuf').get_value()) + #end if + #end if #end for ######################################################## ##Create Connections -## The port name should be the id of the parent block. -## However, port names for IO pads should be self. +## The port name should be the id of the parent block. +## However, port names for IO pads should be self. ######################################################## #def make_port_sig($port) - #if $port.get_parent().get_key() == 'pad_source' + #if $port.get_parent().get_key() == 'pad_source' (self, $flow_graph.get_pad_sources().index($port.get_parent()))#slurp - #elif $port.get_parent().get_key() == 'pad_sink' + #elif $port.get_parent().get_key() == 'pad_sink' (self, $flow_graph.get_pad_sinks().index($port.get_parent()))#slurp - #else + #else (self.$port.get_parent().get_id(), $port.get_key())#slurp - #end if + #end if #end def #if $connections - $DIVIDER - # Connections - $DIVIDER + $DIVIDER + # Connections + $DIVIDER #end if #for $con in $connections - #set $source = $con.get_source() - #set $sink = $con.get_sink() - ##resolve virtual sources to the actual sources - #if $source.get_parent().is_virtual_source() - #set $source = $source.resolve_virtual_source() - #end if - ##do not generate connections with virtual sinks - #if not $sink.get_parent().is_virtual_sink() - self.connect($make_port_sig($source), $make_port_sig($sink)) - #end if + #set $source = $con.get_source() + #set $sink = $con.get_sink() + ##resolve virtual sources to the actual sources + #if $source.get_parent().is_virtual_source() + #set $source = $source.resolve_virtual_source() + #end if + ##do not generate connections with virtual sinks + #if not $sink.get_parent().is_virtual_sink() + self.connect($make_port_sig($source), $make_port_sig($sink)) + #end if #end for ######################################################## ##Create Asynch Message Connections ######################################################## #if $messages2 - $DIVIDER - # Asynch Message Connections - $DIVIDER + $DIVIDER + # Asynch Message Connections + $DIVIDER #end if #for $msg in $messages2 - #set $sr = $msg.get_source() - #set $source = "self.%s"%($sr.get_parent().get_id()) - #set $source_port = $sr.get_name(); - #if $sr.get_parent().get_key() == "pad_source" - #set $source = "self" - #set $source_port = $sr.get_parent().get_param("label").get_value(); - #end if - #set $sk = $msg.get_sink() - #set $sink = "self.%s"%($sk.get_parent().get_id()) - #set $sink_port = $sk.get_name(); - #if $sk.get_parent().get_key() == "pad_sink" - #set $sink = "self" - #set $sink_port = $sk.get_parent().get_param("label").get_value(); - #end if - self.msg_connect($source, "$source_port", $sink, "$sink_port") + #set $sr = $msg.get_source() + #set $source = "self.%s"%($sr.get_parent().get_id()) + #set $source_port = $sr.get_name(); + #if $sr.get_parent().get_key() == "pad_source" + #set $source = "self" + #set $source_port = $sr.get_parent().get_param("label").get_value(); + #end if + #set $sk = $msg.get_sink() + #set $sink = "self.%s"%($sk.get_parent().get_id()) + #set $sink_port = $sk.get_name(); + #if $sk.get_parent().get_key() == "pad_sink" + #set $sink = "self" + #set $sink_port = $sk.get_parent().get_param("label").get_value(); + #end if + self.msg_connect($source, "$source_port", $sink, "$sink_port") #end for ######################################################## # QT sink close method reimplementation ######################################################## #if $generate_options == 'qt_gui' - def closeEvent(self, event): - self.settings = Qt.QSettings("GNU Radio", "$class_name") - self.settings.setValue("geometry", self.saveGeometry()) - event.accept() + def closeEvent(self, event): + self.settings = Qt.QSettings("GNU Radio", "$class_name") + self.settings.setValue("geometry", self.saveGeometry()) + event.accept() #end if ######################################################## ##Create Callbacks -## Write a set method for this variable that calls the callbacks +## Write a set method for this variable that calls the callbacks ######################################################## #for $var in $parameters + $variables - #set $id = $var.get_id() - def get_$(id)(self): - return self.$id + #set $id = $var.get_id() + def get_$(id)(self): + return self.$id - def set_$(id)(self, $id): - self.$id = $id - #for $callback in $var_id2cbs[$id] - $indent($callback) - #end for + def set_$(id)(self, $id): + self.$id = $id + #for $callback in $var_id2cbs[$id] + $indent($callback) + #end for #end for ######################################################## ##Create Main -## For top block code, generate a main routine. -## Instantiate the top block and run as gui or cli. +## For top block code, generate a main routine. +## Instantiate the top block and run as gui or cli. ######################################################## #def make_default($type, $param) - #if $type == 'eng_float' + #if $type == 'eng_float' eng_notation.num_to_str($param.get_make())#slurp - #else + #else $param.get_make()#slurp - #end if + #end if #end def #def make_short_id($param) - #set $short_id = $param.get_param('short_id').get_evaluated() - #if $short_id - #set $short_id = '-' + $short_id - #end if + #set $short_id = $param.get_param('short_id').get_evaluated() + #if $short_id + #set $short_id = '-' + $short_id + #end if $short_id#slurp #end def #if $generate_options != 'hb' if __name__ == '__main__': - parser = OptionParser(option_class=eng_option, usage="%prog: [options]") - #set $params_eq_list = list() - #for $param in $parameters - #set $type = $param.get_param('type').get_value() - #if $type - #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) - parser.add_option("$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), - help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") - #end if - #end for - (options, args) = parser.parse_args() - #if $flow_graph.get_option('realtime_scheduling') - if gr.enable_realtime_scheduling() != gr.RT_OK: - print "Error: failed to enable realtime scheduling." - #end if - #if $generate_options == 'wx_gui' - tb = $(class_name)($(', '.join($params_eq_list))) - #if $flow_graph.get_option('max_nouts') - tb.Run($flow_graph.get_option('run'), $flow_graph.get_option('max_nouts')) - #else - tb.Start($flow_graph.get_option('run')) + parser = OptionParser(option_class=eng_option, usage="%prog: [options]") + #set $params_eq_list = list() + #for $param in $parameters + #set $type = $param.get_param('type').get_value() + #if $type + #silent $params_eq_list.append('%s=options.%s'%($param.get_id(), $param.get_id())) + parser.add_option("$make_short_id($param)", "--$param.get_id().replace('_', '-')", dest="$param.get_id()", type="$type", default=$make_default($type, $param), + help="Set $($param.get_param('label').get_evaluated() or $param.get_id()) [default=%default]") + #end if + #end for + (options, args) = parser.parse_args() + #if $flow_graph.get_option('realtime_scheduling') + if gr.enable_realtime_scheduling() != gr.RT_OK: + print "Error: failed to enable realtime scheduling." + #end if + #if $generate_options == 'wx_gui' + tb = $(class_name)($(', '.join($params_eq_list))) + #if $flow_graph.get_option('max_nouts') + tb.Run($flow_graph.get_option('run'), $flow_graph.get_option('max_nouts')) + #else + tb.Start($flow_graph.get_option('run')) #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - tb.Wait() - #end if - #elif $generate_options == 'qt_gui' - qapp = Qt.QApplication(sys.argv) - tb = $(class_name)($(', '.join($params_eq_list))) - #if $flow_graph.get_option('run') - #if $flow_graph.get_option('max_nouts') - tb.start($flow_graph.get_option('max_nouts')) - #else - tb.start() - #end if - #end if - tb.show() + tb.Wait() + #end if + #elif $generate_options == 'qt_gui' + qapp = Qt.QApplication(sys.argv) + tb = $(class_name)($(', '.join($params_eq_list))) + #if $flow_graph.get_option('run') + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else + tb.start() + #end if + #end if + tb.show() #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - qapp.exec_() - tb.stop() - tb = None #to clean up Qt widgets - #elif $generate_options == 'no_gui' - tb = $(class_name)($(', '.join($params_eq_list))) - #set $run_options = $flow_graph.get_option('run_options') - #if $run_options == 'prompt' - #if $flow_graph.get_option('max_nouts') - tb.start($flow_graph.get_option('max_nouts')) - #else - tb.start() - #end if + qapp.exec_() + tb.stop() + tb = None #to clean up Qt widgets + #elif $generate_options == 'no_gui' + tb = $(class_name)($(', '.join($params_eq_list))) + #set $run_options = $flow_graph.get_option('run_options') + #if $run_options == 'prompt' + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else + tb.start() + #end if #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - raw_input('Press Enter to quit: ') - tb.stop() - #elif $run_options == 'run' - #if $flow_graph.get_option('max_nouts') - tb.start($flow_graph.get_option('max_nouts')) - #else - tb.start() - #end if - #end if + raw_input('Press Enter to quit: ') + tb.stop() + #elif $run_options == 'run' + #if $flow_graph.get_option('max_nouts') + tb.start($flow_graph.get_option('max_nouts')) + #else + tb.start() + #end if + #end if #for $m in $monitors - (tb.$m.get_id()).start() + (tb.$m.get_id()).start() #end for - tb.wait() - #end if + tb.wait() + #end if #end if diff --git a/grc/scripts/gnuradio-companion b/grc/scripts/gnuradio-companion index dabca3028f..6d45ecc246 100755 --- a/grc/scripts/gnuradio-companion +++ b/grc/scripts/gnuradio-companion @@ -24,36 +24,36 @@ import gtk try: from gnuradio import gr except ImportError, e: - d = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE, message_format=""" + d = gtk.MessageDialog(type=gtk.MESSAGE_ERROR, buttons=gtk.BUTTONS_CLOSE, message_format=""" Cannot import gnuradio. Is the python path environment variable set correctly? - All OS: PYTHONPATH + All OS: PYTHONPATH Is the library path environment variable set correctly? - Linux: LD_LIBRARY_PATH - Windows: PATH - MacOSX: DYLD_LIBRARY_PATH + Linux: LD_LIBRARY_PATH + Windows: PATH + MacOSX: DYLD_LIBRARY_PATH """) - d.set_title(str(e)) - d.run() - exit(-1) + d.set_title(str(e)) + d.run() + exit(-1) from optparse import OptionParser import os if __name__ == "__main__": - if ('GR_DONT_LOAD_PREFS' in os.environ.keys() and - (not 'GRC_BLOCKS_PATH' in os.environ.keys() or len(os.environ['GRC_BLOCKS_PATH']) == 0)): - d = gtk.MessageDialog( - type=gtk.MESSAGE_ERROR, - buttons=gtk.BUTTONS_CLOSE, - message_format="""Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH. """) - d.set_title("No block definitions available.") - d.run() - exit(-1) - usage = 'usage: %prog [options] [saved flow graphs]' - version = """ + if ('GR_DONT_LOAD_PREFS' in os.environ.keys() and + (not 'GRC_BLOCKS_PATH' in os.environ.keys() or len(os.environ['GRC_BLOCKS_PATH']) == 0)): + d = gtk.MessageDialog( + type=gtk.MESSAGE_ERROR, + buttons=gtk.BUTTONS_CLOSE, + message_format="""Can't find block definitions. Use config.conf or GRC_BLOCKS_PATH. """) + d.set_title("No block definitions available.") + d.run() + exit(-1) + usage = 'usage: %prog [options] [saved flow graphs]' + version = """ GNU Radio Companion %s This program is part of GNU Radio @@ -61,12 +61,12 @@ GRC comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it. """%gr.version() - parser = OptionParser(usage=usage, version=version) - (options, args) = parser.parse_args() - from gnuradio.grc.python.Platform import Platform - from gnuradio.grc.gui.ActionHandler import ActionHandler - #setup icon using icon theme - try: gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) - except: pass - ActionHandler(args, Platform()) + parser = OptionParser(usage=usage, version=version) + (options, args) = parser.parse_args() + from gnuradio.grc.python.Platform import Platform + from gnuradio.grc.gui.ActionHandler import ActionHandler + #setup icon using icon theme + try: gtk.window_set_default_icon(gtk.IconTheme().load_icon('gnuradio-grc', 256, 0)) + except: pass + ActionHandler(args, Platform()) |