diff options
25 files changed, 1143 insertions, 370 deletions
diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index a320f86cb..e14f49933 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -45,6 +45,8 @@ The user may set the receive antenna to be TX/RX or RX2. However, when using an RFX board in full-duplex mode, the receive antenna will always be set to RX2, regardless of the settings. +Recieve Gains: **PGA0**, Range: 0-45dB + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ XCVR 2450 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -63,6 +65,14 @@ The XCVR2450 uses a common LO for both receive and transmit. Even though the API allows the RX and TX LOs to be individually set, a change of one LO setting will be reflected in the other LO setting. +Transmit Gains: + * **VGA**, Range: 0-30dB + * **BB**, Range: 0-5dB + +Recieve Gains: + * **LNA**, Range: 0-30.5dB + * **VGA**, Range: 0-62dB + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ WBX Series ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,5 +81,9 @@ Transmit Antennas: **TX/RX** Receive Antennas: **TX/RX** or **RX2** The user may set the receive antenna to be TX/RX or RX2. -However, when using an RFX board in full-duplex mode, +However, when using an WBX board in full-duplex mode, the receive antenna will always be set to RX2, regardless of the settings. + +Transmit Gains: **PGA0**, Range: 0-25dB + +Recieve Gains: **PGA0**, Range: 0-31.5dB diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index fea95145c..b23a4dc00 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -31,10 +31,11 @@ # pragma warning(disable: 4251) // class 'A<T>' needs to have dll-interface to be used by clients of class 'B' //# pragma warning(disable: 4127) // conditional expression is constant //# pragma warning(disable: 4290) // C++ exception specification ignored except to ... -//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored +# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; ignored # pragma warning(disable: 4275) // non dll-interface class ... used as base for dll-interface class ... //# pragma warning(disable: 4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data //# pragma warning(disable: 4511) // 'class' : copy constructor could not be generated +# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance #endif //define logical operators diff --git a/host/include/uhd/transport/CMakeLists.txt b/host/include/uhd/transport/CMakeLists.txt index 4cefffa24..23a4aae94 100644 --- a/host/include/uhd/transport/CMakeLists.txt +++ b/host/include/uhd/transport/CMakeLists.txt @@ -17,6 +17,8 @@ INSTALL(FILES + alignment_buffer.hpp + bounded_buffer.hpp convert_types.hpp if_addrs.hpp udp_simple.hpp diff --git a/host/include/uhd/transport/alignment_buffer.hpp b/host/include/uhd/transport/alignment_buffer.hpp new file mode 100644 index 000000000..dc6ccc3ed --- /dev/null +++ b/host/include/uhd/transport/alignment_buffer.hpp @@ -0,0 +1,134 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/condition_variable.hpp> +#include <boost/shared_ptr.hpp> +#include <utility> +#include <vector> + +namespace uhd{ namespace transport{ + + /*! + * Imlement a templated alignment buffer: + * Used for aligning asynchronously pushed elements with matching ids. + */ + template <typename elem_type, typename seq_type> class alignment_buffer{ + public: + typedef boost::shared_ptr<alignment_buffer<elem_type, seq_type> > sptr; + + /*! + * Make a new alignment buffer object. + * \param capacity the maximum elements per index + * \param width the number of elements to align + */ + static sptr make(size_t capacity, size_t width){ + return sptr(new alignment_buffer(capacity, width)); + } + + /*! + * Push an element with sequence id into the buffer at index. + * \param elem the element to push + * \param seq the sequence identifier + * \param index the buffer index + * \return true if the element fit without popping for space + */ + UHD_INLINE bool push_with_pop_on_full( + const elem_type &elem, + const seq_type &seq, + size_t index + ){ + return _buffs[index]->push_with_pop_on_full(buff_contents_type(elem, seq)); + } + + /*! + * Pop an aligned set of elements from this alignment buffer. + * \param elems a collection to store the aligned elements + * \param time the timeout time + * \return false when the operation times out + */ + template <typename elems_type, typename time_type> + bool pop_elems_with_timed_wait(elems_type &elems, const time_type &time){ + buff_contents_type buff_contents_tmp; + std::list<size_t> indexes_to_do(_all_indexes); + + //do an initial pop to load an initial sequence id + size_t index = indexes_to_do.front(); + if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + elems[index] = buff_contents_tmp.first; + seq_type expected_seq_id = buff_contents_tmp.second; + indexes_to_do.pop_front(); + + //get an aligned set of elements from the buffers: + while(indexes_to_do.size() != 0){ + //pop an element off for this index + index = indexes_to_do.front(); + if (not _buffs[index]->pop_with_timed_wait(buff_contents_tmp, time)) return false; + + //if the sequence id matches: + // store the popped element into the output, + // remove this index from the list and continue + if (buff_contents_tmp.second == expected_seq_id){ + elems[index] = buff_contents_tmp.first; + indexes_to_do.pop_front(); + continue; + } + + //if the sequence id is older: + // continue with the same index to try again + if (buff_contents_tmp.second < expected_seq_id){ + continue; + } + + //if the sequence id is newer: + // store the popped element into the output, + // add all other indexes back into the list + if (buff_contents_tmp.second > expected_seq_id){ + elems[index] = buff_contents_tmp.first; + expected_seq_id = buff_contents_tmp.second; + indexes_to_do = _all_indexes; + indexes_to_do.remove(index); + continue; + } + } + return true; + } + + private: + //a vector of bounded buffers for each index + typedef std::pair<elem_type, seq_type> buff_contents_type; + typedef bounded_buffer<buff_contents_type> bounded_buffer_type; + typedef boost::shared_ptr<bounded_buffer_type> bounded_buffer_sptr; + std::vector<bounded_buffer_sptr> _buffs; + std::list<size_t> _all_indexes; + + //private constructor + alignment_buffer(size_t capacity, size_t width){ + for (size_t i = 0; i < width; i++){ + _buffs.push_back(bounded_buffer_type::make(capacity)); + _all_indexes.push_back(i); + } + } + }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_ALIGNMENT_BUFFER_HPP */ diff --git a/host/include/uhd/transport/bounded_buffer.hpp b/host/include/uhd/transport/bounded_buffer.hpp new file mode 100644 index 000000000..baecd6382 --- /dev/null +++ b/host/include/uhd/transport/bounded_buffer.hpp @@ -0,0 +1,146 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP +#define INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP + +#include <uhd/config.hpp> +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/circular_buffer.hpp> +#include <boost/thread/condition.hpp> + +namespace uhd{ namespace transport{ + + /*! + * Imlement a templated bounded buffer: + * Used for passing elements between threads in a producer-consumer model. + * The bounded buffer implemented waits and timed waits with condition variables. + * The pop operation blocks on the bounded_buffer to become non empty. + * The push operation blocks on the bounded_buffer to become non full. + */ + template <typename elem_type> class bounded_buffer{ + public: + typedef boost::shared_ptr<bounded_buffer<elem_type> > sptr; + + /*! + * Make a new bounded buffer object. + * \param capacity the bounded_buffer capacity + */ + static sptr make(size_t capacity){ + return sptr(new bounded_buffer(capacity)); + } + + /*! + * Push a new element into the bounded buffer. + * If the buffer is full prior to the push, + * make room by poping the oldest element. + * \param elem the new element to push + * \return true if the element fit without popping for space + */ + UHD_INLINE bool push_with_pop_on_full(const elem_type &elem){ + boost::unique_lock<boost::mutex> lock(_mutex); + if(_buffer.full()){ + _buffer.pop_back(); + _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); + return false; + } + else{ + _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); + return true; + } + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full. + * \param elem the new element to push + */ + UHD_INLINE void push_with_wait(const elem_type &elem){ + boost::unique_lock<boost::mutex> lock(_mutex); + _full_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_full, this)); + _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full or timeout. + * \param elem the new element to push + * \param time the timeout time + * \return false when the operation times out + */ + template<typename time_type> UHD_INLINE + bool push_with_timed_wait(const elem_type &elem, const time_type &time){ + boost::unique_lock<boost::mutex> lock(_mutex); + if (not _full_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_full, this))) return false; + _buffer.push_front(elem); + lock.unlock(); + _empty_cond.notify_one(); + return true; + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty. + * \param elem the element reference pop to + */ + UHD_INLINE void pop_with_wait(elem_type &elem){ + boost::unique_lock<boost::mutex> lock(_mutex); + _empty_cond.wait(lock, boost::bind(&bounded_buffer<elem_type>::not_empty, this)); + elem = _buffer.back(); _buffer.pop_back(); + lock.unlock(); + _full_cond.notify_one(); + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty or timeout. + * \param elem the element reference pop to + * \param time the timeout time + * \return false when the operation times out + */ + template<typename time_type> UHD_INLINE + bool pop_with_timed_wait(elem_type &elem, const time_type &time){ + boost::unique_lock<boost::mutex> lock(_mutex); + if (not _empty_cond.timed_wait(lock, time, boost::bind(&bounded_buffer<elem_type>::not_empty, this))) return false; + elem = _buffer.back(); _buffer.pop_back(); + lock.unlock(); + _full_cond.notify_one(); + return true; + } + + private: + boost::mutex _mutex; + boost::condition _empty_cond, _full_cond; + boost::circular_buffer<elem_type> _buffer; + + bool not_full(void) const{return not _buffer.full();} + bool not_empty(void) const{return not _buffer.empty();} + + //private constructor + bounded_buffer(size_t capacity) : _buffer(capacity){} + }; + +}} //namespace + +#endif /* INCLUDED_UHD_TRANSPORT_BOUNDED_BUFFER_HPP */ diff --git a/host/include/uhd/transport/udp_zero_copy.hpp b/host/include/uhd/transport/udp_zero_copy.hpp index 525606a9f..818709973 100644 --- a/host/include/uhd/transport/udp_zero_copy.hpp +++ b/host/include/uhd/transport/udp_zero_copy.hpp @@ -34,7 +34,7 @@ namespace uhd{ namespace transport{ * If no platform specific solution is available, make returns a boost asio * implementation that wraps the functionality around a standard send/recv calls. */ -class UHD_API udp_zero_copy : public zero_copy_if{ +class UHD_API udp_zero_copy : public virtual zero_copy_if{ public: typedef boost::shared_ptr<udp_zero_copy> sptr; diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 52c6d4143..2815e3189 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -19,115 +19,201 @@ #define INCLUDED_UHD_TRANSPORT_ZERO_COPY_HPP #include <uhd/config.hpp> +#include <uhd/utils/pimpl.hpp> #include <boost/asio/buffer.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> namespace uhd{ namespace transport{ -/*! - * A managed receive buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after reading. - */ -class UHD_API managed_recv_buffer : boost::noncopyable{ -public: - typedef boost::shared_ptr<managed_recv_buffer> sptr; - - /*! - * Managed recv buffer destructor: - * Signal to the transport that we are done with the buffer. - * This should be called to release the buffer to the transport. - * After calling, the referenced memory should be considered invalid. - */ - virtual ~managed_recv_buffer(void){}; - - /*! - * Get the size of the underlying buffer. - * \return the number of bytes - */ - size_t size(void) const{ - return boost::asio::buffer_size(this->get()); - } - - /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory - */ - template <class T> T cast(void) const{ - return boost::asio::buffer_cast<T>(this->get()); - } - -private: - /*! - * Get a reference to the internal const buffer. - * The buffer has a reference to memory and a size. - * \return a boost asio const buffer - */ - virtual const boost::asio::const_buffer &get(void) const = 0; -}; - -/*! - * A managed send buffer: - * Contains a reference to transport-managed memory, - * and a method to release the memory after writing. - */ -class UHD_API managed_send_buffer : boost::noncopyable{ -public: - typedef boost::shared_ptr<managed_send_buffer> sptr; - - /*! - * Signal to the transport that we are done with the buffer. - * This should be called to commit the write to the transport object. - * After calling, the referenced memory should be considered invalid. - * \param num_bytes the number of bytes written into the buffer - */ - virtual void commit(size_t num_bytes) = 0; - /*! - * Get the size of the underlying buffer. - * \return the number of bytes + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. */ - size_t size(void) const{ - return boost::asio::buffer_size(this->get()); - } + class UHD_API managed_recv_buffer : boost::noncopyable{ + public: + typedef boost::shared_ptr<managed_recv_buffer> sptr; + + /*! + * Managed recv buffer destructor: + * Signal to the transport that we are done with the buffer. + * This should be called to release the buffer to the transport. + * After calling, the referenced memory should be considered invalid. + */ + virtual ~managed_recv_buffer(void) = 0; + + /*! + * Get the size of the underlying buffer. + * \return the number of bytes + */ + size_t size(void) const{ + return boost::asio::buffer_size(this->get()); + } + + /*! + * Get a pointer to the underlying buffer. + * \return a pointer into memory + */ + template <class T> T cast(void) const{ + return boost::asio::buffer_cast<T>(this->get()); + } + + private: + /*! + * Get a reference to the internal const buffer. + * The buffer has a reference to memory and a size. + * \return a boost asio const buffer + */ + virtual const boost::asio::const_buffer &get(void) const = 0; + }; /*! - * Get a pointer to the underlying buffer. - * \return a pointer into memory + * A managed send buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after writing. */ - template <class T> T cast(void) const{ - return boost::asio::buffer_cast<T>(this->get()); - } + class UHD_API managed_send_buffer : boost::noncopyable{ + public: + typedef boost::shared_ptr<managed_send_buffer> sptr; + + /*! + * Signal to the transport that we are done with the buffer. + * This should be called to commit the write to the transport object. + * After calling, the referenced memory should be considered invalid. + * \param num_bytes the number of bytes written into the buffer + */ + virtual void commit(size_t num_bytes) = 0; + + /*! + * Get the size of the underlying buffer. + * \return the number of bytes + */ + size_t size(void) const{ + return boost::asio::buffer_size(this->get()); + } + + /*! + * Get a pointer to the underlying buffer. + * \return a pointer into memory + */ + template <class T> T cast(void) const{ + return boost::asio::buffer_cast<T>(this->get()); + } + + private: + /*! + * Get a reference to the internal mutable buffer. + * The buffer has a reference to memory and a size. + * \return a boost asio mutable buffer + */ + virtual const boost::asio::mutable_buffer &get(void) const = 0; + }; -private: /*! - * Get a reference to the internal mutable buffer. - * The buffer has a reference to memory and a size. - * \return a boost asio mutable buffer + * A zero-copy interface for transport objects. + * Provides a way to get send and receive buffers + * with memory managed by the transport object. */ - virtual const boost::asio::mutable_buffer &get(void) const = 0; -}; - -/*! - * A zero-copy interface for transport objects. - * Provides a way to get send and receive buffers - * with memory managed by the transport object. - */ -class UHD_API zero_copy_if : boost::noncopyable{ -public: - typedef boost::shared_ptr<zero_copy_if> sptr; + class UHD_API zero_copy_if : boost::noncopyable{ + public: + typedef boost::shared_ptr<zero_copy_if> sptr; + + /*! + * Get a new receive buffer from this transport object. + */ + virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + + /*! + * Get the maximum number of receive frames: + * The maximum number of valid managed recv buffers, + * or the maximum number of frames in the ring buffer, + * depending upon the underlying implementation. + * \return number of frames + */ + virtual size_t get_num_recv_frames(void) const = 0; + + /*! + * Get a new send buffer from this transport object. + */ + virtual managed_send_buffer::sptr get_send_buff(void) = 0; + + /*! + * Get the maximum number of send frames: + * The maximum number of valid managed send buffers, + * or the maximum number of frames in the ring buffer, + * depending upon the underlying implementation. + * \return number of frames + */ + virtual size_t get_num_send_frames(void) const = 0; + + }; /*! - * Get a new receive buffer from this transport object. + * A phony-zero-copy interface for transport objects that + * provides a zero-copy interface on top of copying transport. + * This interface implements the get managed recv buffer, + * the base class must implement the private recv method. */ - virtual managed_recv_buffer::sptr get_recv_buff(void) = 0; + class UHD_API phony_zero_copy_recv_if : public virtual zero_copy_if{ + public: + /*! + * Create a phony zero copy recv interface. + * \param max_buff_size max buffer size in bytes + */ + phony_zero_copy_recv_if(size_t max_buff_size); + + //! destructor + virtual ~phony_zero_copy_recv_if(void); + + /*! + * Get a new receive buffer from this transport object. + */ + managed_recv_buffer::sptr get_recv_buff(void); + + private: + /*! + * Perform a private copying recv. + * \param buff the buffer to write data into + * \return the number of bytes written to buff + */ + virtual size_t recv(const boost::asio::mutable_buffer &buff) = 0; + + UHD_PIMPL_DECL(impl) _impl; + }; /*! - * Get a new send buffer from this transport object. + * A phony-zero-copy interface for transport objects that + * provides a zero-copy interface on top of copying transport. + * This interface implements the get managed send buffer, + * the base class must implement the private send method. */ - virtual managed_send_buffer::sptr get_send_buff(void) = 0; -}; + class UHD_API phony_zero_copy_send_if : public virtual zero_copy_if{ + public: + /*! + * Create a phony zero copy send interface. + * \param max_buff_size max buffer size in bytes + */ + phony_zero_copy_send_if(size_t max_buff_size); + + //! destructor + virtual ~phony_zero_copy_send_if(void); + + /*! + * Get a new send buffer from this transport object. + */ + managed_send_buffer::sptr get_send_buff(void); + + private: + /*! + * Perform a private copying send. + * \param buff the buffer to read data from + * \return the number of bytes read from buff + */ + virtual size_t send(const boost::asio::const_buffer &buff) = 0; + + UHD_PIMPL_DECL(impl) _impl; + }; }} //namespace diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index 28bf2ae66..e88d39876 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -20,6 +20,7 @@ #include <uhd/config.hpp> #include <uhd/wax.hpp> +#include <uhd/utils/pimpl.hpp> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp> #include <uhd/usrp/dboard_id.hpp> @@ -58,8 +59,7 @@ protected: dboard_id_t get_tx_id(void); private: - struct dboard_base_impl; - dboard_base_impl *_impl; + UHD_PIMPL_DECL(impl) _impl; }; /*! diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index f588c6310..391e684c8 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -20,6 +20,7 @@ INSTALL(FILES assert.hpp exception.hpp gain_handler.hpp + pimpl.hpp props.hpp safe_main.hpp static.hpp diff --git a/host/include/uhd/utils/exception.hpp b/host/include/uhd/utils/exception.hpp index 40e81fae0..e74c19b9c 100644 --- a/host/include/uhd/utils/exception.hpp +++ b/host/include/uhd/utils/exception.hpp @@ -35,4 +35,11 @@ " at " + std::string(__FILE__) + ":" + BOOST_STRINGIZE(__LINE__) + "\n" \ ) +/*! + * Throws an invalid code path exception with throw-site information. + * Use this macro in places that code execution is not supposed to go. + */ +#define UHD_THROW_INVALID_CODE_PATH() \ + throw std::runtime_error(UHD_THROW_SITE_INFO("invalid code path")) + #endif /* INCLUDED_UHD_UTILS_EXCEPTION_HPP */ diff --git a/host/include/uhd/utils/pimpl.hpp b/host/include/uhd/utils/pimpl.hpp new file mode 100644 index 000000000..09bf0c0a2 --- /dev/null +++ b/host/include/uhd/utils/pimpl.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_PIMPL_HPP +#define INCLUDED_UHD_UTILS_PIMPL_HPP + +#include <uhd/config.hpp> +#include <boost/shared_ptr.hpp> + +/*! \file pimpl.hpp + * "Pimpl idiom" (pointer to implementation idiom). + * The UHD_PIMPL_* macros simplify code overhead for declaring and making pimpls. + * + * Each pimpl is implemented as a shared pointer to the implementation: + * - The container class will not have to deallocate the pimpl. + * - The container class will use the pimpl as a regular pointer. + * - Usage: _impl->method(arg0, arg1) + * - Usage: _impl->member = value; + * + * \see http://en.wikipedia.org/wiki/Opaque_pointer + */ + +/*! + * Make a declaration for a pimpl in a header file. + * - Usage: UHD_PIMPL_DECL(impl) _impl; + * \param _name the name of the pimpl class + */ +#define UHD_PIMPL_DECL(_name) \ + struct _name; boost::shared_ptr<_name> + +/*! + * Make an instance of a pimpl in a source file. + * - Usage: _impl = UHD_PIMPL_MAKE(impl, ()); + * - Usage: _impl = UHD_PIMPL_MAKE(impl, (a0, a1)); + * \param _name the name of the pimpl class + * \param _args the constructor args for the pimpl + */ +#define UHD_PIMPL_MAKE(_name, _args) \ + boost::shared_ptr<_name>(new _name _args) + +#endif /* INCLUDED_UHD_UTILS_PIMPL_HPP */ diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index ed8c35225..a74f7d527 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -50,4 +50,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp ${CMAKE_SOURCE_DIR}/lib/transport/udp_zero_copy_asio.cpp ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler.hpp + ${CMAKE_SOURCE_DIR}/lib/transport/zero_copy.cpp ) diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index f8a222475..ced606777 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -28,58 +28,8 @@ using namespace uhd::transport; * Constants **********************************************************************/ static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); -static const size_t MAX_DGRAM_SIZE = 2048; //assume max size on send and recv -static const double RECV_TIMEOUT = 0.1; // 100 ms - -/*********************************************************************** - * Managed receive buffer implementation for udp zero-copy asio: - **********************************************************************/ -class managed_recv_buffer_impl : public managed_recv_buffer{ -public: - managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ - /* NOP */ - } - - ~managed_recv_buffer_impl(void){ - delete [] this->cast<const boost::uint8_t *>(); - } - -private: - const boost::asio::const_buffer &get(void) const{ - return _buff; - } - - const boost::asio::const_buffer _buff; -}; - -/*********************************************************************** - * Managed send buffer implementation for udp zero-copy asio: - **********************************************************************/ -class managed_send_buffer_impl : public managed_send_buffer{ -public: - managed_send_buffer_impl( - const boost::asio::mutable_buffer &buff, - boost::asio::ip::udp::socket *socket - ) : _buff(buff), _socket(socket){ - /* NOP */ - } - - ~managed_send_buffer_impl(void){ - /* NOP */ - } - - void commit(size_t num_bytes){ - _socket->send(boost::asio::buffer(_buff, num_bytes)); - } - -private: - const boost::asio::mutable_buffer &get(void) const{ - return _buff; - } - - const boost::asio::mutable_buffer _buff; - boost::asio::ip::udp::socket *_socket; -}; +static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv +static const double RECV_TIMEOUT = 0.1; //100 ms /*********************************************************************** * Zero Copy UDP implementation with ASIO: @@ -88,95 +38,108 @@ private: * However, it is not a true zero copy implementation as each * send and recv requires a copy operation to/from userspace. **********************************************************************/ -class udp_zero_copy_impl : public udp_zero_copy{ +class udp_zero_copy_impl: + public phony_zero_copy_recv_if, + public phony_zero_copy_send_if, + public udp_zero_copy +{ public: typedef boost::shared_ptr<udp_zero_copy_impl> sptr; - //structors - udp_zero_copy_impl(const std::string &addr, const std::string &port); - ~udp_zero_copy_impl(void); + udp_zero_copy_impl( + const std::string &addr, + const std::string &port + ): + phony_zero_copy_recv_if(MAX_DGRAM_SIZE), + phony_zero_copy_send_if(MAX_DGRAM_SIZE) + { + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); + _sock_fd = _socket->native(); + } - //send/recv - managed_recv_buffer::sptr get_recv_buff(void); - managed_send_buffer::sptr get_send_buff(void); + ~udp_zero_copy_impl(void){ + delete _socket; + } - //manage buffer - template <typename Opt> size_t get_buff_size(void){ + //get size for internal socket buffer + template <typename Opt> size_t get_buff_size(void) const{ Opt option; _socket->get_option(option); return option.value(); } + //set size for internal socket buffer template <typename Opt> size_t resize_buff(size_t num_bytes){ Opt option(num_bytes); _socket->set_option(option); return get_buff_size<Opt>(); } -private: - boost::asio::ip::udp::socket *_socket; - boost::asio::io_service _io_service; - - //send and recv buffer memory (allocated once) - boost::uint8_t _send_mem[MIN_SOCK_BUFF_SIZE]; - - managed_send_buffer::sptr _send_buff; -}; -udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); - - // create, open, and connect the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - _socket->connect(receiver_endpoint); - - // create the managed send buff (just once) - _send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( - boost::asio::buffer(_send_mem, MIN_SOCK_BUFF_SIZE), _socket - )); - - // set recv timeout - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = size_t(RECV_TIMEOUT*1e6); - UHD_ASSERT_THROW(setsockopt( - _socket->native(), - SOL_SOCKET, SO_RCVTIMEO, - (const char *)&tv, sizeof(timeval) - ) == 0); -} + //The number of frames is approximately the buffer size divided by the max datagram size. + //In reality, this is a phony zero-copy interface and the number of frames is infinite. + //However, its sensible to advertise a frame count that is approximate to buffer size. + //This way, the transport caller will have an idea about how much buffering to create. -udp_zero_copy_impl::~udp_zero_copy_impl(void){ - delete _socket; -} - -managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){ - //allocate memory - boost::uint8_t *recv_mem = new boost::uint8_t[MAX_DGRAM_SIZE]; + size_t get_num_recv_frames(void) const{ + return this->get_buff_size<boost::asio::socket_base::receive_buffer_size>()/MAX_DGRAM_SIZE; + } - //call recv() with timeout option - size_t num_bytes = _socket->receive(boost::asio::buffer(recv_mem, MIN_SOCK_BUFF_SIZE)); + size_t get_num_send_frames(void) const{ + return this->get_buff_size<boost::asio::socket_base::send_buffer_size>()/MAX_DGRAM_SIZE; + } - //create a new managed buffer to house the data - return managed_recv_buffer::sptr( - new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) - ); -} +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::io_service _io_service; + int _sock_fd; + + size_t recv(const boost::asio::mutable_buffer &buff){ + //setup timeval for timeout + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = int(RECV_TIMEOUT*1e6); + + //setup rset for timeout + fd_set rset; + FD_ZERO(&rset); + FD_SET(_sock_fd, &rset); + + //call select to perform timed wait + if (::select(_sock_fd+1, &rset, NULL, NULL, &tv) <= 0) return 0; + + return ::recv( + _sock_fd, + boost::asio::buffer_cast<char *>(buff), + boost::asio::buffer_size(buff), + 0 + ); + } -managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ - return _send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these -} + size_t send(const boost::asio::const_buffer &buff){ + return ::send( + _sock_fd, + boost::asio::buffer_cast<const char *>(buff), + boost::asio::buffer_size(buff), + 0 + ); + } +}; /*********************************************************************** * UDP zero copy make function **********************************************************************/ -template<typename Opt> static inline void resize_buff_helper( +template<typename Opt> static void resize_buff_helper( udp_zero_copy_impl::sptr udp_trans, size_t target_size, const std::string &name diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp index e64e3383d..d6b863040 100644 --- a/host/lib/transport/vrt_packet_handler.hpp +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -54,6 +54,8 @@ namespace vrt_packet_handler{ } }; + typedef boost::function<uhd::transport::managed_recv_buffer::sptr(void)> get_recv_buff_t; + typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t; static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){ @@ -112,15 +114,16 @@ namespace vrt_packet_handler{ const uhd::io_type_t &io_type, const uhd::otw_type_t &otw_type, double tick_rate, - uhd::transport::zero_copy_if::sptr zc_iface, + const get_recv_buff_t &get_recv_buff, //use these two params to handle a layer above vrt size_t vrt_header_offset_words32, - const recv_cb_t& recv_cb + const recv_cb_t &recv_cb ){ //perform a receive if no rx data is waiting to be copied if (boost::asio::buffer_size(state.copy_buff) == 0){ state.fragment_offset_in_samps = 0; - state.managed_buff = zc_iface->get_recv_buff(); + state.managed_buff = get_recv_buff(); + if (state.managed_buff.get() == NULL) return 0; recv_cb(state.managed_buff); //callback before vrt unpack try{ _recv1_helper( @@ -169,7 +172,7 @@ namespace vrt_packet_handler{ const uhd::io_type_t &io_type, const uhd::otw_type_t &otw_type, double tick_rate, - uhd::transport::zero_copy_if::sptr zc_iface, + const get_recv_buff_t &get_recv_buff, //use these two params to handle a layer above vrt size_t vrt_header_offset_words32 = 0, const recv_cb_t& recv_cb = &recv_cb_nop @@ -189,7 +192,7 @@ namespace vrt_packet_handler{ metadata, io_type, otw_type, tick_rate, - zc_iface, + get_recv_buff, vrt_header_offset_words32, recv_cb ); @@ -208,7 +211,7 @@ namespace vrt_packet_handler{ (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept io_type, otw_type, tick_rate, - zc_iface, + get_recv_buff, vrt_header_offset_words32, recv_cb ); @@ -234,6 +237,8 @@ namespace vrt_packet_handler{ } }; + typedef boost::function<uhd::transport::managed_send_buffer::sptr(void)> get_send_buff_t; + typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t; static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){ @@ -252,12 +257,12 @@ namespace vrt_packet_handler{ const uhd::io_type_t &io_type, const uhd::otw_type_t &otw_type, double tick_rate, - uhd::transport::zero_copy_if::sptr zc_iface, + const get_send_buff_t &get_send_buff, size_t vrt_header_offset_words32, const send_cb_t& send_cb ){ //get a new managed send buffer - uhd::transport::managed_send_buffer::sptr send_buff = zc_iface->get_send_buff(); + uhd::transport::managed_send_buffer::sptr send_buff = get_send_buff(); boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32; size_t num_header_words32, num_packet_words32; @@ -298,7 +303,7 @@ namespace vrt_packet_handler{ const uhd::io_type_t &io_type, const uhd::otw_type_t &otw_type, double tick_rate, - uhd::transport::zero_copy_if::sptr zc_iface, + const get_send_buff_t &get_send_buff, size_t max_samples_per_packet, //use these two params to handle a layer above vrt size_t vrt_header_offset_words32 = 0, @@ -319,7 +324,7 @@ namespace vrt_packet_handler{ metadata, io_type, otw_type, tick_rate, - zc_iface, + get_send_buff, vrt_header_offset_words32, send_cb ); @@ -353,7 +358,7 @@ namespace vrt_packet_handler{ md, io_type, otw_type, tick_rate, - zc_iface, + get_send_buff, vrt_header_offset_words32, send_cb ); diff --git a/host/lib/transport/zero_copy.cpp b/host/lib/transport/zero_copy.cpp new file mode 100644 index 000000000..27f41329b --- /dev/null +++ b/host/lib/transport/zero_copy.cpp @@ -0,0 +1,139 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/function.hpp> +#include <boost/bind.hpp> + +using namespace uhd::transport; + +/*********************************************************************** + * The pure-virtual deconstructor needs an implementation to be happy + **********************************************************************/ +managed_recv_buffer::~managed_recv_buffer(void){ + /* NOP */ +} + +/*********************************************************************** + * Phony zero-copy recv interface implementation + **********************************************************************/ + +//! phony zero-copy recv buffer implementation +class managed_recv_buffer_impl : public managed_recv_buffer{ +public: + managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ + /* NOP */ + } + + ~managed_recv_buffer_impl(void){ + delete [] this->cast<const boost::uint8_t *>(); + } + +private: + const boost::asio::const_buffer &get(void) const{ + return _buff; + } + + const boost::asio::const_buffer _buff; +}; + +//! phony zero-copy recv interface implementation +struct phony_zero_copy_recv_if::impl{ + size_t max_buff_size; +}; + +phony_zero_copy_recv_if::phony_zero_copy_recv_if(size_t max_buff_size){ + _impl = UHD_PIMPL_MAKE(impl, ()); + _impl->max_buff_size = max_buff_size; +} + +phony_zero_copy_recv_if::~phony_zero_copy_recv_if(void){ + /* NOP */ +} + +managed_recv_buffer::sptr phony_zero_copy_recv_if::get_recv_buff(void){ + //allocate memory + boost::uint8_t *recv_mem = new boost::uint8_t[_impl->max_buff_size]; + + //call recv() with timeout option + size_t num_bytes = this->recv(boost::asio::buffer(recv_mem, _impl->max_buff_size)); + + //create a new managed buffer to house the data + return managed_recv_buffer::sptr( + new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) + ); +} + +/*********************************************************************** + * Phony zero-copy send interface implementation + **********************************************************************/ + +//! phony zero-copy send buffer implementation +class managed_send_buffer_impl : public managed_send_buffer{ +public: + typedef boost::function<size_t(const boost::asio::const_buffer &)> send_fcn_t; + + managed_send_buffer_impl( + const boost::asio::mutable_buffer &buff, + const send_fcn_t &send_fcn + ): + _buff(buff), + _send_fcn(send_fcn) + { + /* NOP */ + } + + ~managed_send_buffer_impl(void){ + /* NOP */ + } + + void commit(size_t num_bytes){ + _send_fcn(boost::asio::buffer(_buff, num_bytes)); + } + +private: + const boost::asio::mutable_buffer &get(void) const{ + return _buff; + } + + const boost::asio::mutable_buffer _buff; + const send_fcn_t _send_fcn; +}; + +//! phony zero-copy send interface implementation +struct phony_zero_copy_send_if::impl{ + boost::uint8_t *send_mem; + managed_send_buffer::sptr send_buff; +}; + +phony_zero_copy_send_if::phony_zero_copy_send_if(size_t max_buff_size){ + _impl = UHD_PIMPL_MAKE(impl, ()); + _impl->send_mem = new boost::uint8_t[max_buff_size]; + _impl->send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( + boost::asio::buffer(_impl->send_mem, max_buff_size), + boost::bind(&phony_zero_copy_send_if::send, this, _1) + )); +} + +phony_zero_copy_send_if::~phony_zero_copy_send_if(void){ + delete [] _impl->send_mem; +} + +managed_send_buffer::sptr phony_zero_copy_send_if::get_send_buff(void){ + return _impl->send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these +} diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 89e707718..17fc00d24 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -91,7 +91,7 @@ private: uhd::dict<dboard_iface::unit_t, bool> _div2; double _rx_lo_freq, _tx_lo_freq; std::string _rx_ant; - float _rx_pga0_gain; + uhd::dict<std::string, float> _rx_gains; void set_rx_lo_freq(double freq); void set_tx_lo_freq(double freq); @@ -100,8 +100,6 @@ private: void set_rx_gain(float gain, const std::string &name); void set_tx_gain(float gain, const std::string &name); - void set_rx_pga0_gain(float gain); - /*! * Set the LO frequency for the particular dboard unit. * \param unit which unit rx or tx @@ -198,7 +196,10 @@ rfx_xcvr::rfx_xcvr( set_rx_lo_freq((_freq_range.min + _freq_range.max)/2.0); set_tx_lo_freq((_freq_range.min + _freq_range.max)/2.0); set_rx_ant("RX2"); - set_rx_pga0_gain(0); + + BOOST_FOREACH(const std::string &name, rfx_rx_gain_ranges.keys()){ + set_rx_gain(rfx_rx_gain_ranges[name].min, name); + } } rfx_xcvr::~rfx_xcvr(void){ @@ -230,20 +231,7 @@ void rfx_xcvr::set_tx_ant(const std::string &ant){ /*********************************************************************** * Gain Handling **********************************************************************/ -void rfx_xcvr::set_tx_gain(float, const std::string &name){ - assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); - UHD_ASSERT_THROW(false); //no gains to set -} - -void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ - assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); - if(name == "PGA0"){ - this->set_rx_pga0_gain(gain); - } - else UHD_ASSERT_THROW(false); -} - -void rfx_xcvr::set_rx_pga0_gain(float gain){ +static float rx_pga0_gain_to_dac_volts(float &gain){ //voltage level constants (negative slope) static const float max_volts = float(.2), min_volts = float(1.2); static const float slope = (max_volts-min_volts)/45; @@ -251,11 +239,27 @@ void rfx_xcvr::set_rx_pga0_gain(float gain){ //calculate the voltage for the aux dac float dac_volts = std::clip<float>(gain*slope + min_volts, max_volts, min_volts); - //write the new voltage to the aux dac - this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); + //the actual gain setting + gain = (dac_volts - min_volts)/slope; + + return dac_volts; +} + +void rfx_xcvr::set_tx_gain(float, const std::string &name){ + assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); + UHD_THROW_INVALID_CODE_PATH(); //no gains to set +} + +void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ + assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); + if(name == "PGA0"){ + float dac_volts = rx_pga0_gain_to_dac_volts(gain); + _rx_gains[name] = gain; - //shadow the actual gain setting - _rx_pga0_gain = (dac_volts - min_volts)/slope; + //write the new voltage to the aux dac + this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); + } + else UHD_THROW_INVALID_CODE_PATH(); } /*********************************************************************** @@ -397,8 +401,8 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - val = _rx_pga0_gain; + assert_has(_rx_gains.keys(), name, "rfx rx gain name"); + val = _rx_gains[name]; return; case SUBDEV_PROP_GAIN_RANGE: diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index 23654860f..95dcb3802 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -15,8 +15,6 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -static const bool wbx_debug = false; - // Common IO Pins #define ANTSW_IO ((1 << 5)|(1 << 15)) // on UNIT_TX, 0 = TX, 1 = RX, on UNIT_RX 0 = main ant, 1 = RX2 #define ADF4350_CE (1 << 3) @@ -84,17 +82,30 @@ using namespace uhd::usrp; using namespace boost::assign; /*********************************************************************** - * The WBX dboard + * The WBX dboard constants **********************************************************************/ -static const float _max_rx_pga0_gain = 31.5; -static const float _max_tx_pga0_gain = 25; +static const bool wbx_debug = false; + +static const freq_range_t wbx_freq_range(50e6, 2.22e9); + +static const prop_names_t wbx_tx_antennas = list_of("TX/RX"); +static const prop_names_t wbx_rx_antennas = list_of("TX/RX")("RX2"); + +static const uhd::dict<std::string, gain_range_t> wbx_tx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 25, float(0.05))) +; + +static const uhd::dict<std::string, gain_range_t> wbx_rx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 31.5, float(0.5))) +; + +/*********************************************************************** + * The WBX dboard + **********************************************************************/ class wbx_xcvr : public xcvr_dboard_base{ public: - wbx_xcvr( - ctor_args_t args, - const freq_range_t &freq_range - ); + wbx_xcvr(ctor_args_t args); ~wbx_xcvr(void); void rx_get(const wax::obj &key, wax::obj &val); @@ -104,20 +115,16 @@ public: void tx_set(const wax::obj &key, const wax::obj &val); private: - freq_range_t _freq_range; - uhd::dict<dboard_iface::unit_t, bool> _div2; + uhd::dict<std::string, float> _tx_gains, _rx_gains; double _rx_lo_freq, _tx_lo_freq; - std::string _rx_ant; - int _rx_pga0_attn_iobits; - float _rx_pga0_gain; - float _tx_pga0_gain; + std::string _tx_ant, _rx_ant; void set_rx_lo_freq(double freq); void set_tx_lo_freq(double freq); void set_rx_ant(const std::string &ant); - void set_rx_pga0_gain(float gain); - void set_rx_pga0_attn(float attn); - void set_tx_pga0_gain(float gain); + void set_tx_ant(const std::string &ant); + void set_rx_gain(float gain, const std::string &name); + void set_tx_gain(float gain, const std::string &name); void update_atr(void); @@ -143,7 +150,7 @@ private: * Register the WBX dboard (min freq, max freq, rx div2, tx div2) **********************************************************************/ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){ - return dboard_base::sptr(new wbx_xcvr(args, freq_range_t(50e6, 2220e6))); + return dboard_base::sptr(new wbx_xcvr(args)); } UHD_STATIC_BLOCK(reg_wbx_dboards){ @@ -154,11 +161,7 @@ UHD_STATIC_BLOCK(reg_wbx_dboards){ /*********************************************************************** * Structors **********************************************************************/ -wbx_xcvr::wbx_xcvr( - ctor_args_t args, - const freq_range_t &freq_range -) : xcvr_dboard_base(args){ - _freq_range = freq_range; +wbx_xcvr::wbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //enable the clocks that we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); @@ -174,11 +177,16 @@ wbx_xcvr::wbx_xcvr( ) % RXIO_MASK % TXIO_MASK << std::endl; //set some default values - set_rx_lo_freq((_freq_range.min + _freq_range.max)/2.0); - set_tx_lo_freq((_freq_range.min + _freq_range.max)/2.0); + set_rx_lo_freq((wbx_freq_range.min + wbx_freq_range.max)/2.0); + set_tx_lo_freq((wbx_freq_range.min + wbx_freq_range.max)/2.0); set_rx_ant("RX2"); - set_rx_pga0_gain(0); - set_tx_pga0_gain(0); + + BOOST_FOREACH(const std::string &name, wbx_tx_gain_ranges.keys()){ + set_tx_gain(wbx_tx_gain_ranges[name].min, name); + } + BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){ + set_rx_gain(wbx_rx_gain_ranges[name].min, name); + } } wbx_xcvr::~wbx_xcvr(void){ @@ -186,10 +194,81 @@ wbx_xcvr::~wbx_xcvr(void){ } /*********************************************************************** - * Helper Methods + * Gain Handling + **********************************************************************/ +static int rx_pga0_gain_to_iobits(float &gain){ + //clip the input + gain = std::clip<float>(gain, wbx_rx_gain_ranges["PGA0"].min, wbx_rx_gain_ranges["PGA0"].max); + + //convert to attenuation and update iobits for atr + float attn = wbx_rx_gain_ranges["PGA0"].max - gain; + + //calculate the attenuation + int attn_code = int(floor(attn*2)); + int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; + + + if (wbx_debug) std::cerr << boost::format( + "WBX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; + + //the actual gain setting + gain = wbx_rx_gain_ranges["PGA0"].max - float(attn_code)/2; + + return iobits; +} + +static float tx_pga0_gain_to_dac_volts(float &gain){ + //clip the input + gain = std::clip<float>(gain, wbx_tx_gain_ranges["PGA0"].min, wbx_tx_gain_ranges["PGA0"].max); + + //voltage level constants + static const float max_volts = float(0.5), min_volts = float(1.4); + static const float slope = (max_volts-min_volts)/wbx_tx_gain_ranges["PGA0"].max; + + //calculate the voltage for the aux dac + float dac_volts = gain*slope + min_volts; + + if (wbx_debug) std::cerr << boost::format( + "WBX TX Gain: %f dB, dac_volts: %f V" + ) % gain % dac_volts << std::endl; + + //the actual gain setting + gain = (dac_volts - min_volts)/slope; + + return dac_volts; +} + +void wbx_xcvr::set_tx_gain(float gain, const std::string &name){ + assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); + if(name == "PGA0"){ + float dac_volts = tx_pga0_gain_to_dac_volts(gain); + _tx_gains[name] = gain; + + //write the new voltage to the aux dac + this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); + } + else UHD_THROW_INVALID_CODE_PATH(); +} + +void wbx_xcvr::set_rx_gain(float gain, const std::string &name){ + assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name"); + if(name == "PGA0"){ + rx_pga0_gain_to_iobits(gain); + _rx_gains[name] = gain; + + //write the new gain to atr regs + update_atr(); + } + else UHD_THROW_INVALID_CODE_PATH(); +} + +/*********************************************************************** + * Antenna Handling **********************************************************************/ void wbx_xcvr::update_atr(void){ //calculate atr pins + int pga0_iobits = rx_pga0_gain_to_iobits(_rx_gains["PGA0"]); //setup the tx atr (this does not change with antenna) this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, TX_POWER_UP | ANT_XX | TX_MIXER_DIS); @@ -199,31 +278,23 @@ void wbx_xcvr::update_atr(void){ //setup the rx atr (this does not change with antenna) this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, - _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); + pga0_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, - _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); + pga0_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, - _rx_pga0_attn_iobits | RX_POWER_UP | ANT_RX2| RX_MIXER_ENB); + pga0_iobits | RX_POWER_UP | ANT_RX2| RX_MIXER_ENB); //set the rx atr regs that change with antenna setting this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, - _rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); + pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); if (wbx_debug) std::cerr << boost::format( "WBX RXONLY ATR REG: 0x%08x" - ) % (_rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; -} - -void wbx_xcvr::set_rx_lo_freq(double freq){ - _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void wbx_xcvr::set_tx_lo_freq(double freq){ - _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); + ) % (pga0_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; } void wbx_xcvr::set_rx_ant(const std::string &ant){ //validate input - UHD_ASSERT_THROW(ant == "TX/RX" or ant == "RX2"); + assert_has(wbx_rx_antennas, ant, "wbx rx antenna name"); //shadow the setting _rx_ant = ant; @@ -232,49 +303,20 @@ void wbx_xcvr::set_rx_ant(const std::string &ant){ update_atr(); } -void wbx_xcvr::set_rx_pga0_gain(float gain){ - //clip the input - gain = std::clip<float>(gain, 0, _max_rx_pga0_gain); - - //shadow the setting (does not account for precision loss) - _rx_pga0_gain = gain; - - //convert to attenuation and update iobits for atr - set_rx_pga0_attn(_max_rx_pga0_gain - gain); - - //write the new gain to atr regs - update_atr(); +void wbx_xcvr::set_tx_ant(const std::string &ant){ + assert_has(wbx_tx_antennas, ant, "wbx tx antenna name"); + //only one antenna option, do nothing } -void wbx_xcvr::set_rx_pga0_attn(float attn) -{ - int attn_code = int(floor(attn*2)); - _rx_pga0_attn_iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; - if (wbx_debug) std::cerr << boost::format( - "WBX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" - ) % attn % attn_code % (_rx_pga0_attn_iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; +/*********************************************************************** + * Tuning + **********************************************************************/ +void wbx_xcvr::set_rx_lo_freq(double freq){ + _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); } -void wbx_xcvr::set_tx_pga0_gain(float gain){ - //clip the input - gain = std::clip<float>(gain, 0, _max_tx_pga0_gain); - - //voltage level constants - static const float max_volts = float(0.5), min_volts = float(1.4); - static const float slope = (max_volts-min_volts)/_max_rx_pga0_gain; - - //calculate the voltage for the aux dac - float dac_volts = gain*slope + min_volts; - - if (wbx_debug) std::cerr << boost::format( - "WBX TX Gain: %f dB, dac_volts: %f V" - ) % gain % dac_volts << std::endl; - - //write the new voltage to the aux dac - this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); - - //shadow the setting (does not account for precision loss) - _tx_pga0_gain = gain; +void wbx_xcvr::set_tx_lo_freq(double freq){ + _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); } double wbx_xcvr::set_lo_freq( @@ -286,7 +328,7 @@ double wbx_xcvr::set_lo_freq( ) % (target_freq/1e6) << std::endl; //clip the input - target_freq = std::clip(target_freq, _freq_range.min, _freq_range.max); + target_freq = std::clip(target_freq, wbx_freq_range.min, wbx_freq_range.max); //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of @@ -439,17 +481,17 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - val = _rx_pga0_gain; + assert_has(_rx_gains.keys(), name, "wbx rx gain name"); + val = _rx_gains[name]; return; case SUBDEV_PROP_GAIN_RANGE: - UHD_ASSERT_THROW(name == "PGA0"); - val = gain_range_t(0, _max_rx_pga0_gain, float(0.5)); + assert_has(wbx_rx_gain_ranges.keys(), name, "wbx rx gain name"); + val = wbx_rx_gain_ranges[name]; return; case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(1, "PGA0"); + val = prop_names_t(wbx_rx_gain_ranges.keys()); return; case SUBDEV_PROP_FREQ: @@ -457,17 +499,15 @@ void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_FREQ_RANGE: - val = _freq_range; + val = wbx_freq_range; return; case SUBDEV_PROP_ANTENNA: val = _rx_ant; return; - case SUBDEV_PROP_ANTENNA_NAMES:{ - prop_names_t ants = list_of("TX/RX")("RX2"); - val = ants; - } + case SUBDEV_PROP_ANTENNA_NAMES: + val = wbx_rx_antennas; return; case SUBDEV_PROP_QUADRATURE: @@ -502,16 +542,15 @@ void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_FREQ: - set_rx_lo_freq(val.as<double>()); + this->set_rx_lo_freq(val.as<double>()); return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - set_rx_pga0_gain(val.as<float>()); + this->set_rx_gain(val.as<float>(), name); return; case SUBDEV_PROP_ANTENNA: - set_rx_ant(val.as<std::string>()); + this->set_rx_ant(val.as<std::string>()); return; default: UHD_THROW_PROP_SET_ERROR(); @@ -536,17 +575,17 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - val = _tx_pga0_gain; + assert_has(_tx_gains.keys(), name, "wbx tx gain name"); + val = _tx_gains[name]; return; case SUBDEV_PROP_GAIN_RANGE: - UHD_ASSERT_THROW(name == "PGA0"); - val = gain_range_t(0, _max_tx_pga0_gain, float(0.05)); + assert_has(wbx_tx_gain_ranges.keys(), name, "wbx tx gain name"); + val = wbx_tx_gain_ranges[name]; return; case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(1, "PGA0"); + val = prop_names_t(wbx_tx_gain_ranges.keys()); return; case SUBDEV_PROP_FREQ: @@ -554,7 +593,7 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_FREQ_RANGE: - val = _freq_range; + val = wbx_freq_range; return; case SUBDEV_PROP_ANTENNA: @@ -562,7 +601,7 @@ void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, "TX/RX"); + val = wbx_tx_antennas; return; case SUBDEV_PROP_QUADRATURE: @@ -597,17 +636,15 @@ void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_FREQ: - set_tx_lo_freq(val.as<double>()); + this->set_tx_lo_freq(val.as<double>()); return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - set_tx_pga0_gain(val.as<float>()); + this->set_tx_gain(val.as<float>(), name); return; case SUBDEV_PROP_ANTENNA: - //its always set to tx/rx, so we only allow this value - UHD_ASSERT_THROW(val.as<std::string>() == "TX/RX"); + this->set_tx_ant(val.as<std::string>()); return; default: UHD_THROW_PROP_SET_ERROR(); diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index d4d5f184e..974a378bd 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -375,7 +375,7 @@ static max2829_regs_t::tx_baseband_gain_t gain_to_tx_bb_reg(float &gain){ gain = 5; return max2829_regs_t::TX_BASEBAND_GAIN_5DB; } - UHD_ASSERT_THROW(false); + UHD_THROW_INVALID_CODE_PATH(); } /*! @@ -417,7 +417,7 @@ void xcvr2450::set_tx_gain(float gain, const std::string &name){ _max2829_regs.tx_baseband_gain = gain_to_tx_bb_reg(gain); send_reg(0x9); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); _tx_gains[name] = gain; } @@ -431,7 +431,7 @@ void xcvr2450::set_rx_gain(float gain, const std::string &name){ _max2829_regs.rx_lna_gain = gain_to_rx_lna_reg(gain); send_reg(0xB); } - else UHD_ASSERT_THROW(false); + else UHD_THROW_INVALID_CODE_PATH(); _rx_gains[name] = gain; } diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index bd4b37ef3..eafb8897f 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -25,17 +25,17 @@ using namespace uhd::usrp; /*********************************************************************** * dboard_base dboard dboard_base class **********************************************************************/ -struct dboard_base::dboard_base_impl{ +struct dboard_base::impl{ ctor_args_impl args; - dboard_base_impl(ctor_args_t args) : args(*args){} + impl(ctor_args_t args) : args(*args){} }; dboard_base::dboard_base(ctor_args_t args){ - _impl = new dboard_base_impl(args); + _impl = UHD_PIMPL_MAKE(impl, (args)); } dboard_base::~dboard_base(void){ - delete _impl; + /* NOP */ } std::string dboard_base::get_subdev_name(void){ diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 01352039e..35ddfc4ee 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -181,7 +181,7 @@ static args_t get_dboard_args( switch(unit){ case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0x0001); case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0x0000); - default: UHD_ASSERT_THROW(false); + default: UHD_THROW_INVALID_CODE_PATH(); } } diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 0a2e4b550..2621d43b4 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -136,7 +136,7 @@ void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){ //calculate the new selection mux setting boost::uint32_t new_sels = 0x0; for(size_t i = 0; i < 16; i++){ - bool is_bit_set = bool(value & (0x1 << i)); + bool is_bit_set = (value & (0x1 << i)) != 0; new_sels |= ((is_bit_set)? FRF_GPIO_SEL_ATR : FRF_GPIO_SEL_GPIO) << (i*2); } diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 79b18fb63..18f2d013f 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -15,11 +15,15 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "../../transport/vrt_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" #include <uhd/transport/convert_types.hpp> +#include <uhd/transport/bounded_buffer.hpp> #include <boost/format.hpp> #include <boost/asio.hpp> //htonl and ntohl +#include <boost/bind.hpp> +#include <boost/thread.hpp> #include <iostream> using namespace uhd; @@ -28,6 +32,60 @@ using namespace uhd::transport; namespace asio = boost::asio; /*********************************************************************** + * io impl details (internal to this file) + **********************************************************************/ +struct usrp2_impl::io_impl{ + + io_impl(zero_copy_if::sptr zc_if); + ~io_impl(void); + + managed_recv_buffer::sptr get_recv_buff(void); + + //state management for the vrt packet handler code + vrt_packet_handler::recv_state packet_handler_recv_state; + vrt_packet_handler::send_state packet_handler_send_state; + + //methods and variables for the recv pirate + void recv_pirate_loop(zero_copy_if::sptr zc_if); + boost::thread *recv_pirate_thread; bool recv_pirate_running; + bounded_buffer<managed_recv_buffer::sptr>::sptr recv_pirate_booty; +}; + +usrp2_impl::io_impl::io_impl(zero_copy_if::sptr zc_if){ + //create a large enough booty + size_t num_frames = zc_if->get_num_recv_frames(); + std::cout << "Recv pirate num frames: " << num_frames << std::endl; + recv_pirate_booty = bounded_buffer<managed_recv_buffer::sptr>::make(num_frames); + + //create a new pirate thread (yarr!!) + recv_pirate_thread = new boost::thread( + boost::bind(&usrp2_impl::io_impl::recv_pirate_loop, this, zc_if) + ); +} + +usrp2_impl::io_impl::~io_impl(void){ + recv_pirate_running = false; + recv_pirate_thread->interrupt(); + recv_pirate_thread->join(); + delete recv_pirate_thread; +} + +managed_recv_buffer::sptr usrp2_impl::io_impl::get_recv_buff(void){ + managed_recv_buffer::sptr buff; + boost::this_thread::disable_interruption di; //disable because the wait can throw + recv_pirate_booty->pop_with_timed_wait(buff, boost::posix_time::milliseconds(100)); + return buff; //a timeout means that we return a null sptr... +} + +void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){ + recv_pirate_running = true; + while(recv_pirate_running){ + managed_recv_buffer::sptr buff = zc_if->get_recv_buff(); + if (buff->size()) recv_pirate_booty->push_with_pop_on_full(buff); + } +} + +/*********************************************************************** * Helper Functions **********************************************************************/ void usrp2_impl::io_init(void){ @@ -60,6 +118,11 @@ void usrp2_impl::io_init(void){ ); _iface->poke32(FR_RX_CTRL_VRT_STREAM_ID, 0); _iface->poke32(FR_RX_CTRL_VRT_TRAILER, 0); + + std::cout << "TX samples per packet: " << get_max_send_samps_per_packet() << std::endl; + + //create new io impl + _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); } /*********************************************************************** @@ -72,11 +135,11 @@ size_t usrp2_impl::send( send_mode_t send_mode ){ return vrt_packet_handler::send( - _packet_handler_send_state, //last state of the send handler + _io_impl->packet_handler_send_state, //last state of the send handler buff, metadata, send_mode, //buffer to empty and samples metadata io_type, _tx_otw_type, //input and output types to convert get_master_clock_freq(), //master clock tick rate - _data_transport, //zero copy interface + boost::bind(&zero_copy_if::get_send_buff, _data_transport), get_max_send_samps_per_packet() ); } @@ -91,10 +154,10 @@ size_t usrp2_impl::recv( recv_mode_t recv_mode ){ return vrt_packet_handler::recv( - _packet_handler_recv_state, //last state of the recv handler + _io_impl->packet_handler_recv_state, //last state of the recv handler buff, metadata, recv_mode, //buffer to fill and samples metadata io_type, _rx_otw_type, //input and output types to convert get_master_clock_freq(), //master clock tick rate - _data_transport //zero copy interface + boost::bind(&usrp2_impl::io_impl::get_recv_buff, _io_impl) ); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 7948a2069..40c193866 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -23,6 +23,7 @@ #include "codec_ctrl.hpp" #include "serdes_ctrl.hpp" #include <uhd/usrp/usrp2.hpp> +#include <uhd/utils/pimpl.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/otw_type.hpp> #include <uhd/types/stream_cmd.hpp> @@ -33,7 +34,6 @@ #include <uhd/transport/vrt.hpp> #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/usrp/dboard_manager.hpp> -#include "../../transport/vrt_packet_handler.hpp" /*! * Make a usrp2 dboard interface. @@ -153,9 +153,8 @@ private: uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t) ; - vrt_packet_handler::recv_state _packet_handler_recv_state; - vrt_packet_handler::send_state _packet_handler_send_state; uhd::otw_type_t _rx_otw_type, _tx_otw_type; + UHD_PIMPL_DECL(io_impl) _io_impl; void io_init(void); //udp transports for control and data diff --git a/host/test/CMakeLists.txt b/host/test/CMakeLists.txt index 61b0b503d..24778d13e 100644 --- a/host/test/CMakeLists.txt +++ b/host/test/CMakeLists.txt @@ -21,6 +21,7 @@ ADD_EXECUTABLE(main_test main_test.cpp addr_test.cpp + buffer_test.cpp dict_test.cpp error_test.cpp gain_handler_test.cpp diff --git a/host/test/buffer_test.cpp b/host/test/buffer_test.cpp new file mode 100644 index 000000000..aadb3f951 --- /dev/null +++ b/host/test/buffer_test.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program 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 of the License, or +// (at your option) any later version. +// +// This program 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 this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/test/unit_test.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/alignment_buffer.hpp> +#include <boost/assign/list_of.hpp> + +using namespace boost::assign; +using namespace uhd::transport; + +static const boost::posix_time::milliseconds timeout(10); + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_timed_wait){ + bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_timed_wait(0, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(1, timeout)); + BOOST_CHECK(bb->push_with_timed_wait(2, timeout)); + BOOST_CHECK(not bb->push_with_timed_wait(3, timeout)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 0); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(not bb->pop_with_timed_wait(val, timeout)); +} + +BOOST_AUTO_TEST_CASE(test_bounded_buffer_with_pop_on_full){ + bounded_buffer<int>::sptr bb(bounded_buffer<int>::make(3)); + + //push elements, check for timeout + BOOST_CHECK(bb->push_with_pop_on_full(0)); + BOOST_CHECK(bb->push_with_pop_on_full(1)); + BOOST_CHECK(bb->push_with_pop_on_full(2)); + BOOST_CHECK(not bb->push_with_pop_on_full(3)); + + int val; + //pop elements, check for timeout and check values + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 1); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 2); + BOOST_CHECK(bb->pop_with_timed_wait(val, timeout)); + BOOST_CHECK_EQUAL(val, 3); +} + +BOOST_AUTO_TEST_CASE(test_alignment_buffer){ + alignment_buffer<int, size_t>::sptr ab(alignment_buffer<int, size_t>::make(7, 3)); + //load index 0 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(0, 0, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(1, 1, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(2, 2, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(3, 3, 0)); + BOOST_CHECK(ab->push_with_pop_on_full(4, 4, 0)); + + //load index 1 with some skipped seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(10, 0, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(11, 1, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(14, 4, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(15, 5, 1)); + BOOST_CHECK(ab->push_with_pop_on_full(16, 6, 1)); + + //load index 2 with all good seq numbers + BOOST_CHECK(ab->push_with_pop_on_full(20, 0, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(21, 1, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(22, 2, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(23, 3, 2)); + BOOST_CHECK(ab->push_with_pop_on_full(24, 4, 2)); + + //readback aligned values + std::vector<int> aligned_elems(3); + + static const std::vector<int> expected_elems0 = list_of(0)(10)(20); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems0.begin(), expected_elems0.end() + ); + + static const std::vector<int> expected_elems1 = list_of(1)(11)(21); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems1.begin(), expected_elems1.end() + ); + + //there was a skip now find 4 + + static const std::vector<int> expected_elems4 = list_of(4)(14)(24); + BOOST_CHECK(ab->pop_elems_with_timed_wait(aligned_elems, timeout)); + BOOST_CHECK_EQUAL_COLLECTIONS( + aligned_elems.begin(), aligned_elems.end(), + expected_elems4.begin(), expected_elems4.end() + ); +} |