From eec4cd82ff0948652fb073d81b426fa9ce1ab7c5 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Thu, 19 Aug 2010 19:44:01 -0700 Subject: uhd: work on tx waveforms to make it more accurate --- host/examples/tx_waveforms.cpp | 62 +++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 19 deletions(-) (limited to 'host') diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index e9cf210bb..22975da96 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -20,6 +20,7 @@ #include #include #include //system time +#include #include #include #include @@ -32,7 +33,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args, wave_type; - size_t total_duration, amspb; + size_t total_duration, mspb; double rate, freq, wave_freq; float ampl, gain; @@ -42,7 +43,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("help", "help message") ("args", po::value(&args)->default_value(""), "simple uhd device address args") ("duration", po::value(&total_duration)->default_value(3), "number of seconds to transmit") - ("amspb", po::value(&amspb)->default_value(10000), "approximate mimimum samples per buffer") + ("mspb", po::value(&mspb)->default_value(10000), "mimimum samples per buffer") ("rate", po::value(&rate)->default_value(100e6/16), "rate of outgoing samples") ("freq", po::value(&freq)->default_value(0), "rf center frequency in Hz") ("ampl", po::value(&l)->default_value(float(0.3)), "amplitude of the waveform") @@ -88,33 +89,56 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //error when the waveform is not possible to generate - if (std::abs(wave_freq)/sdev->get_tx_rate() < 0.5/amspb){ + if (std::abs(wave_freq)/sdev->get_tx_rate() < 0.5/mspb){ throw std::runtime_error("wave freq/tx rate too small"); } if (std::abs(wave_freq) > sdev->get_tx_rate()/2){ throw std::runtime_error("wave freq out of Nyquist zone"); } - //fill a buffer with one period worth of samples - std::vector period(size_t(sdev->get_tx_rate()/std::abs(wave_freq))); - std::cout << boost::format("Samples per waveform period: %d") % period.size() << std::endl; - for (size_t n = 0; n < period.size(); n++){ - if (wave_type == "CONST") period[n] = ampl; - else if (wave_type == "SQUARE") period[n] = (n > period.size()/2)? ampl : 0; - else if (wave_type == "RAMP") period[n] = float((n/double(period.size()-1)) * 2*ampl - ampl); - else if (wave_type == "SINE") period[n] = ampl*float(std::sin(2*M_PI*n/double(period.size()))); - else throw std::runtime_error("unknown waveform type: " + wave_type); + //how many periods should we have per buffer to mimimize error + double samps_per_period = sdev->get_tx_rate()/std::abs(wave_freq); + std::cout << boost::format("Samples per waveform period: %d") % samps_per_period << std::endl; + size_t periods_per_buff = std::max(1, mspb/samps_per_period); + while (true){ + double num_samps_per_buff = periods_per_buff*samps_per_period; + double sample_error = num_samps_per_buff - boost::math::round(num_samps_per_buff); + if (std::abs(sample_error/num_samps_per_buff) < 1e-5) break; + periods_per_buff++; } //allocate data to send (fill with several periods worth of IQ samples) - const size_t periods_per_buff = std::max(1, amspb/period.size()); - std::vector > buff(period.size()*periods_per_buff); + std::vector > buff(samps_per_period*periods_per_buff); + const double i_ahead = (wave_freq > 0)? samps_per_period/4 : 0; + const double q_ahead = (wave_freq < 0)? samps_per_period/4 : 0; std::cout << boost::format("Samples per send buffer: %d") % buff.size() << std::endl; - const size_t i_ahead = (wave_freq > 0)? period.size()/4 : 0; - const size_t q_ahead = (wave_freq < 0)? period.size()/4 : 0; - for (size_t n = 0; n < buff.size(); n++) buff[n] = std::complex( - period[(n+i_ahead)%period.size()], period[(n+q_ahead)%period.size()] //I,Q - ); + if (wave_type == "CONST"){ + for (size_t n = 0; n < buff.size(); n++){ + buff[n] = std::complex(ampl, ampl); + } + } + else if (wave_type == "SQUARE"){ + for (size_t n = 0; n < buff.size(); n++){ + float I = (std::fmod(n+i_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; + float Q = (std::fmod(n+q_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; + buff[n] = std::complex(I, Q); + } + } + else if (wave_type == "RAMP"){ + for (size_t n = 0; n < buff.size(); n++){ + float I = float(std::fmod(n+i_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); + float Q = float(std::fmod(n+q_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); + buff[n] = std::complex(I, Q); + } + } + else if (wave_type == "SINE"){ + for (size_t n = 0; n < buff.size(); n++){ + float I = float(ampl*std::sin(2*M_PI*(n+i_ahead)/samps_per_period)); + float Q = float(ampl*std::sin(2*M_PI*(n+q_ahead)/samps_per_period)); + buff[n] = std::complex(I, Q); + } + } + else throw std::runtime_error("unknown waveform type: " + wave_type); //setup the metadata flags uhd::tx_metadata_t md; -- cgit v1.2.3 From 605483a2cf38eecdd39222622b9eab93f8061186 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 20 Aug 2010 10:04:02 -0700 Subject: uhd: tx waveform change how error is measured --- host/examples/tx_waveforms.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'host') diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index 22975da96..b9b24f516 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -34,7 +34,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //variables to be set by po std::string args, wave_type; size_t total_duration, mspb; - double rate, freq, wave_freq; + double rate, freq, wave_freq, aepb; float ampl, gain; //setup the program options @@ -44,6 +44,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("args", po::value(&args)->default_value(""), "simple uhd device address args") ("duration", po::value(&total_duration)->default_value(3), "number of seconds to transmit") ("mspb", po::value(&mspb)->default_value(10000), "mimimum samples per buffer") + ("aepb", po::value(&aepb)->default_value(1e-5), "allowed error per buffer") ("rate", po::value(&rate)->default_value(100e6/16), "rate of outgoing samples") ("freq", po::value(&freq)->default_value(0), "rf center frequency in Hz") ("ampl", po::value(&l)->default_value(float(0.3)), "amplitude of the waveform") @@ -103,7 +104,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ while (true){ double num_samps_per_buff = periods_per_buff*samps_per_period; double sample_error = num_samps_per_buff - boost::math::round(num_samps_per_buff); - if (std::abs(sample_error/num_samps_per_buff) < 1e-5) break; + if (std::abs(sample_error) <= aepb) break; periods_per_buff++; } -- cgit v1.2.3 From 6798b4f8d7936cc77a979e8e6d22c24eb5c92678 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 20 Aug 2010 15:35:25 -0700 Subject: uhd: tx waveform generates on the fly now --- host/examples/tx_waveforms.cpp | 111 +++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 54 deletions(-) (limited to 'host') diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index b9b24f516..f9c42b1c9 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -22,19 +22,39 @@ #include //system time #include #include +#include #include #include #include namespace po = boost::program_options; +/*********************************************************************** + * Waveform generators + **********************************************************************/ +float gen_const(float){ + return 1; +} + +float gen_square(float x){ + return (std::fmod(x, 1) < float(0.5))? 0 : 1; +} + +float gen_ramp(float x){ + return std::fmod(x, 1)*2 - 1; +} + +float gen_sine(float x){ + return std::sin(x*2*M_PI); +} + int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::set_thread_priority_safe(); //variables to be set by po std::string args, wave_type; - size_t total_duration, mspb; - double rate, freq, wave_freq, aepb; + size_t total_duration, spb; + double rate, freq, wave_freq; float ampl, gain; //setup the program options @@ -43,8 +63,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("help", "help message") ("args", po::value(&args)->default_value(""), "simple uhd device address args") ("duration", po::value(&total_duration)->default_value(3), "number of seconds to transmit") - ("mspb", po::value(&mspb)->default_value(10000), "mimimum samples per buffer") - ("aepb", po::value(&aepb)->default_value(1e-5), "allowed error per buffer") + ("spb", po::value(&spb)->default_value(10000), "samples per buffer") ("rate", po::value(&rate)->default_value(100e6/16), "rate of outgoing samples") ("freq", po::value(&freq)->default_value(0), "rf center frequency in Hz") ("ampl", po::value(&l)->default_value(float(0.3)), "amplitude of the waveform") @@ -90,57 +109,25 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } //error when the waveform is not possible to generate - if (std::abs(wave_freq)/sdev->get_tx_rate() < 0.5/mspb){ - throw std::runtime_error("wave freq/tx rate too small"); - } if (std::abs(wave_freq) > sdev->get_tx_rate()/2){ throw std::runtime_error("wave freq out of Nyquist zone"); } - //how many periods should we have per buffer to mimimize error - double samps_per_period = sdev->get_tx_rate()/std::abs(wave_freq); - std::cout << boost::format("Samples per waveform period: %d") % samps_per_period << std::endl; - size_t periods_per_buff = std::max(1, mspb/samps_per_period); - while (true){ - double num_samps_per_buff = periods_per_buff*samps_per_period; - double sample_error = num_samps_per_buff - boost::math::round(num_samps_per_buff); - if (std::abs(sample_error) <= aepb) break; - periods_per_buff++; - } - - //allocate data to send (fill with several periods worth of IQ samples) - std::vector > buff(samps_per_period*periods_per_buff); - const double i_ahead = (wave_freq > 0)? samps_per_period/4 : 0; - const double q_ahead = (wave_freq < 0)? samps_per_period/4 : 0; - std::cout << boost::format("Samples per send buffer: %d") % buff.size() << std::endl; - if (wave_type == "CONST"){ - for (size_t n = 0; n < buff.size(); n++){ - buff[n] = std::complex(ampl, ampl); - } - } - else if (wave_type == "SQUARE"){ - for (size_t n = 0; n < buff.size(); n++){ - float I = (std::fmod(n+i_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; - float Q = (std::fmod(n+q_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; - buff[n] = std::complex(I, Q); - } - } - else if (wave_type == "RAMP"){ - for (size_t n = 0; n < buff.size(); n++){ - float I = float(std::fmod(n+i_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); - float Q = float(std::fmod(n+q_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); - buff[n] = std::complex(I, Q); - } - } - else if (wave_type == "SINE"){ - for (size_t n = 0; n < buff.size(); n++){ - float I = float(ampl*std::sin(2*M_PI*(n+i_ahead)/samps_per_period)); - float Q = float(ampl*std::sin(2*M_PI*(n+q_ahead)/samps_per_period)); - buff[n] = std::complex(I, Q); - } - } + //store the generator function for the selected waveform + boost::function wave_gen; + if (wave_type == "CONST") wave_gen = &gen_const; + else if (wave_type == "SQUARE") wave_gen = &gen_square; + else if (wave_type == "RAMP") wave_gen = &gen_ramp; + else if (wave_type == "SINE") wave_gen = &gen_sine; else throw std::runtime_error("unknown waveform type: " + wave_type); + //allocate the buffer and precalculate values + std::vector > buff(spb); + const float cps = wave_freq/sdev->get_tx_rate(); + const float i_off = (wave_freq > 0)? float(0.25) : 0; + const float q_off = (wave_freq < 0)? float(0.25) : 0; + float theta = 0; + //setup the metadata flags uhd::tx_metadata_t md; md.start_of_burst = true; //always SOB (good for continuous streaming) @@ -148,11 +135,27 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //send the data in multiple packets boost::system_time end_time(boost::get_system_time() + boost::posix_time::seconds(total_duration)); - while(end_time > boost::get_system_time()) dev->send( - &buff.front(), buff.size(), md, - uhd::io_type_t::COMPLEX_FLOAT32, - uhd::device::SEND_MODE_FULL_BUFF - ); + while(end_time > boost::get_system_time()){ + + //fill the buffer with the waveform + for (size_t n = 0; n < buff.size(); n++){ + buff[n] = std::complex( + ampl*wave_gen(i_off + theta), + ampl*wave_gen(q_off + theta) + ); + theta += cps; + } + + //bring the theta back into range [0, 1) + theta = std::fmod(theta, 1); + + //send the entire contents of the buffer + dev->send( + &buff.front(), buff.size(), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + } //send a mini EOB packet md.start_of_burst = false; -- cgit v1.2.3 From 8673ee7235bb31f2f51ed2200952f605b0de1955 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Fri, 20 Aug 2010 16:19:41 -0700 Subject: uhd: removed msvc warnings and errors (no M_PI) --- host/examples/tx_waveforms.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'host') diff --git a/host/examples/tx_waveforms.cpp b/host/examples/tx_waveforms.cpp index f9c42b1c9..9886000b1 100644 --- a/host/examples/tx_waveforms.cpp +++ b/host/examples/tx_waveforms.cpp @@ -37,7 +37,7 @@ float gen_const(float){ } float gen_square(float x){ - return (std::fmod(x, 1) < float(0.5))? 0 : 1; + return float((std::fmod(x, 1) < float(0.5))? 0 : 1); } float gen_ramp(float x){ @@ -45,7 +45,8 @@ float gen_ramp(float x){ } float gen_sine(float x){ - return std::sin(x*2*M_PI); + static const float two_pi = 2*std::acos(float(-1)); + return std::sin(x*two_pi); } int UHD_SAFE_MAIN(int argc, char *argv[]){ @@ -123,7 +124,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //allocate the buffer and precalculate values std::vector > buff(spb); - const float cps = wave_freq/sdev->get_tx_rate(); + const float cps = float(wave_freq/sdev->get_tx_rate()); const float i_off = (wave_freq > 0)? float(0.25) : 0; const float q_off = (wave_freq < 0)? float(0.25) : 0; float theta = 0; -- cgit v1.2.3 From 4ecc24d09415d7c0a1c3f2383b6adec08105c571 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Mon, 23 Aug 2010 16:12:12 -0700 Subject: basic: better errors for invalid antenna selection --- host/lib/usrp/dboard/db_basic_and_lf.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'host') diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index f8236d598..0b6e4a75a 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -170,8 +170,8 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ return; case SUBDEV_PROP_ANTENNA: - UHD_ASSERT_THROW(val.as() == std::string("")); - return; + if (val.as().empty()) return; + throw std::runtime_error("no selectable antennas on this board"); case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it @@ -259,8 +259,8 @@ void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ return; case SUBDEV_PROP_ANTENNA: - UHD_ASSERT_THROW(val.as() == std::string("")); - return; + if (val.as().empty()) return; + throw std::runtime_error("no selectable antennas on this board"); case SUBDEV_PROP_FREQ: return; // it wont do you much good, but you can set it -- cgit v1.2.3 From 48ad3b734314bdec2128dacb20b09fd4cf1f5979 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Mon, 23 Aug 2010 19:06:35 -0700 Subject: usrp: added get dboard iface to simple wrapper, and set gpio debug to dboard iface --- host/include/uhd/usrp/dboard_iface.hpp | 8 ++++++++ host/include/uhd/usrp/simple_usrp.hpp | 5 +++++ host/lib/usrp/simple_usrp.cpp | 8 ++++++++ host/lib/usrp/usrp2/dboard_iface.cpp | 20 ++++++++++++++++++++ 4 files changed, 41 insertions(+) (limited to 'host') diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index e776ecc42..c7db244f2 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -147,6 +147,14 @@ public: */ virtual void write_gpio(unit_t unit, boost::uint16_t value) = 0; + /*! + * Setup the GPIO debug mux. + * + * \param unit which unit rx or tx + * \param which which debug: 0, 1 + */ + virtual void set_gpio_debug(unit_t unit, int which) = 0; + /*! * Read daughterboard GPIO pin values. * diff --git a/host/include/uhd/usrp/simple_usrp.hpp b/host/include/uhd/usrp/simple_usrp.hpp index 4da63c929..08b9c01ea 100644 --- a/host/include/uhd/usrp/simple_usrp.hpp +++ b/host/include/uhd/usrp/simple_usrp.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -136,6 +137,8 @@ public: */ virtual float read_rssi(void) = 0; + virtual dboard_iface::sptr get_rx_dboard_iface(void) = 0; + /******************************************************************* * TX methods ******************************************************************/ @@ -159,6 +162,8 @@ public: virtual std::vector get_tx_antennas(void) = 0; virtual bool get_tx_lo_locked(void) = 0; + + virtual dboard_iface::sptr get_tx_dboard_iface(void) = 0; }; }} diff --git a/host/lib/usrp/simple_usrp.cpp b/host/lib/usrp/simple_usrp.cpp index 60b25a647..e573d0fc0 100644 --- a/host/lib/usrp/simple_usrp.cpp +++ b/host/lib/usrp/simple_usrp.cpp @@ -168,6 +168,10 @@ public: return _rx_subdev()[SUBDEV_PROP_RSSI].as(); } + dboard_iface::sptr get_rx_dboard_iface(void){ + return _rx_dboard()[DBOARD_PROP_DBOARD_IFACE].as(); + } + /******************************************************************* * TX methods ******************************************************************/ @@ -232,6 +236,10 @@ public: return _tx_subdev()[SUBDEV_PROP_LO_LOCKED].as(); } + dboard_iface::sptr get_tx_dboard_iface(void){ + return _tx_dboard()[DBOARD_PROP_DBOARD_IFACE].as(); + } + private: device::sptr _dev; wax::obj _mboard(void){ diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 8bded1ea3..f6d2b718a 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -51,6 +51,7 @@ public: void set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); void set_gpio_ddr(unit_t, boost::uint16_t); void write_gpio(unit_t, boost::uint16_t); + void set_gpio_debug(unit_t, int); boost::uint16_t read_gpio(unit_t); void write_i2c(boost::uint8_t, const byte_vector_t &); @@ -219,6 +220,25 @@ void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t _iface->poke16(unit_to_atr_to_addr[unit][atr], value); } +void usrp2_dboard_iface::set_gpio_debug(unit_t unit, int which){ + this->set_gpio_ddr(unit, 0xffff); //all outputs + + //calculate the new selection mux setting + boost::uint32_t new_sels = 0x0; + int sel = (which == 0)? + U2_FLAG_GPIO_SEL_DEBUG_0: + U2_FLAG_GPIO_SEL_DEBUG_1; + for(size_t i = 0; i < 16; i++){ + new_sels |= sel << (i*2); + } + + //write the selection mux value to register + switch(unit){ + case UNIT_RX: _iface->poke32(U2_REG_GPIO_RX_SEL, new_sels); return; + case UNIT_TX: _iface->poke32(U2_REG_GPIO_TX_SEL, new_sels); return; + } +} + /*********************************************************************** * SPI **********************************************************************/ -- cgit v1.2.3 From 8c872ffb2e4f24927b6ec9de825a31c5eda014b8 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Tue, 24 Aug 2010 17:26:07 -0700 Subject: uhd: convert types corrected for little endian, created SSE2 float/short conversion for no-swap case --- host/lib/transport/convert_types_impl.hpp | 141 ++++++++++++++++++++++++------ host/test/convert_types_test.cpp | 139 ++++++++++++++++++----------- 2 files changed, 202 insertions(+), 78 deletions(-) (limited to 'host') diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp index 5958b08cb..641029795 100644 --- a/host/lib/transport/convert_types_impl.hpp +++ b/host/lib/transport/convert_types_impl.hpp @@ -28,6 +28,13 @@ #define USE_EMMINTRIN_H //use sse2 intrinsics #endif +#if defined(USE_EMMINTRIN_H) + #include +#endif + +//! shortcut for a byteswap16 with casting +#define BSWAP16_C(num) uhd::byteswap(boost::uint16_t(num)) + /*********************************************************************** * Typedefs **********************************************************************/ @@ -47,9 +54,10 @@ static UHD_INLINE void sc16_to_item32_nswap( static UHD_INLINE void sc16_to_item32_bswap( const sc16_t *input, item32_t *output, size_t nsamps ){ - const item32_t *item32_input = (const item32_t *)input; for (size_t i = 0; i < nsamps; i++){ - output[i] = uhd::byteswap(item32_input[i]); + boost::uint16_t real = BSWAP16_C(input[i].real()); + boost::uint16_t imag = BSWAP16_C(input[i].imag()); + output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); } } @@ -65,34 +73,71 @@ static UHD_INLINE void item32_to_sc16_nswap( static UHD_INLINE void item32_to_sc16_bswap( const item32_t *input, sc16_t *output, size_t nsamps ){ - item32_t *item32_output = (item32_t *)output; for (size_t i = 0; i < nsamps; i++){ - item32_output[i] = uhd::byteswap(input[i]); + boost::int16_t real = BSWAP16_C(input[i] >> 0); + boost::int16_t imag = BSWAP16_C(input[i] >> 16); + output[i] = sc16_t(real, imag); } } /*********************************************************************** - * Convert complex float buffer to items32 + * Convert complex float buffer to items32 (no swap) **********************************************************************/ static const float shorts_per_float = float(32767); -static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ - boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); - boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); - return (item32_t(real) << 16) | (item32_t(imag) << 0); +#define FC32_TO_SC16_C(num) boost::int16_t(num*shorts_per_float) + +//////////////////////////////////// +// none-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) +static UHD_INLINE void fc32_to_item32_nswap( + const fc32_t *input, item32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(shorts_per_float); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128 tmplo = _mm_loadu_ps(reinterpret_cast(input+i+0)); + __m128 tmphi = _mm_loadu_ps(reinterpret_cast(input+i+2)); + + //convert and scale + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); + + //pack + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); + + //store to output + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); + } + + //convert remainder + for (; i < nsamps; i++){ + boost::uint16_t real = FC32_TO_SC16_C(input[i].real()); + boost::uint16_t imag = FC32_TO_SC16_C(input[i].imag()); + output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + } } +#else static UHD_INLINE void fc32_to_item32_nswap( const fc32_t *input, item32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - output[i] = fc32_to_item32(input[i]); + boost::uint16_t real = FC32_TO_SC16_C(input[i].real()); + boost::uint16_t imag = FC32_TO_SC16_C(input[i].imag()); + output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); } } -#if defined(USE_EMMINTRIN_H) -#include +#endif +//////////////////////////////////// +// byte-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) static UHD_INLINE void fc32_to_item32_bswap( const fc32_t *input, item32_t *output, size_t nsamps ){ @@ -108,7 +153,7 @@ static UHD_INLINE void fc32_to_item32_bswap( __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); - //pack + byteswap -> byteswap 32 bit words + //pack + byteswap -> byteswap 16 bit words __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); @@ -118,7 +163,9 @@ static UHD_INLINE void fc32_to_item32_bswap( //convert remainder for (; i < nsamps; i++){ - output[i] = uhd::byteswap(fc32_to_item32(input[i])); + boost::uint16_t real = BSWAP16_C(FC32_TO_SC16_C(input[i].real())); + boost::uint16_t imag = BSWAP16_C(FC32_TO_SC16_C(input[i].imag())); + output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); } } @@ -127,7 +174,9 @@ static UHD_INLINE void fc32_to_item32_bswap( const fc32_t *input, item32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - output[i] = uhd::byteswap(fc32_to_item32(input[i])); + boost::uint16_t real = BSWAP16_C(FC32_TO_SC16_C(input[i].real())); + boost::uint16_t imag = BSWAP16_C(FC32_TO_SC16_C(input[i].imag())); + output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); } } @@ -138,24 +187,60 @@ static UHD_INLINE void fc32_to_item32_bswap( **********************************************************************/ static const float floats_per_short = float(1.0/shorts_per_float); -static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ - return fc32_t( - float(boost::int16_t(item >> 16)*floats_per_short), - float(boost::int16_t(item >> 0)*floats_per_short) - ); +#define I16_TO_FC32_C(num) (boost::int16_t(num)*floats_per_short) + +//////////////////////////////////// +// none-swap +//////////////////////////////////// +#if defined(USE_EMMINTRIN_H) +static UHD_INLINE void item32_to_fc32_nswap( + const item32_t *input, fc32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); + __m128i zeroi = _mm_setzero_si128(); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128i tmpi = _mm_loadu_si128(reinterpret_cast(input+i)); + + //unpack + __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits + __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); + + //convert and scale + __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); + __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); + + //store to output + _mm_storeu_ps(reinterpret_cast(output+i+0), tmplo); + _mm_storeu_ps(reinterpret_cast(output+i+2), tmphi); + } + + //convert remainder + for (; i < nsamps; i++){ + float real = I16_TO_FC32_C(input[i] >> 0); + float imag = I16_TO_FC32_C(input[i] >> 16); + output[i] = fc32_t(real, imag); + } } +#else static UHD_INLINE void item32_to_fc32_nswap( const item32_t *input, fc32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - output[i] = item32_to_fc32(input[i]); + float real = I16_TO_FC32_C(input[i] >> 0); + float imag = I16_TO_FC32_C(input[i] >> 16); + output[i] = fc32_t(real, imag); } } +#endif +//////////////////////////////////// +// byte-swap +//////////////////////////////////// #if defined(USE_EMMINTRIN_H) -#include - static UHD_INLINE void item32_to_fc32_bswap( const item32_t *input, fc32_t *output, size_t nsamps ){ @@ -167,7 +252,7 @@ static UHD_INLINE void item32_to_fc32_bswap( //load from input __m128i tmpi = _mm_loadu_si128(reinterpret_cast(input+i)); - //byteswap + unpack -> byteswap 32 bit words + //byteswap + unpack -> byteswap 16 bit words tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); @@ -183,7 +268,9 @@ static UHD_INLINE void item32_to_fc32_bswap( //convert remainder for (; i < nsamps; i++){ - output[i] = item32_to_fc32(uhd::byteswap(input[i])); + float real = I16_TO_FC32_C(BSWAP16_C(input[i] >> 0)); + float imag = I16_TO_FC32_C(BSWAP16_C(input[i] >> 16)); + output[i] = fc32_t(real, imag); } } @@ -192,7 +279,9 @@ static UHD_INLINE void item32_to_fc32_bswap( const item32_t *input, fc32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - output[i] = item32_to_fc32(uhd::byteswap(input[i])); + float real = I16_TO_FC32_C(BSWAP16_C(input[i] >> 0)); + float imag = I16_TO_FC32_C(BSWAP16_C(input[i] >> 16)); + output[i] = fc32_t(real, imag); } } diff --git a/host/test/convert_types_test.cpp b/host/test/convert_types_test.cpp index 1587be57f..d132a708b 100644 --- a/host/test/convert_types_test.cpp +++ b/host/test/convert_types_test.cpp @@ -17,109 +17,144 @@ #include #include +#include #include +#include #include +#include +#include using namespace uhd; -template -void loopback( +//typedefs for complex types +typedef std::complex sc16_t; +typedef std::complex fc32_t; + +//extract pointer to POD since using &vector.front() throws in MSVC +template void * pod2ptr(T &pod){ + return boost::asio::buffer_cast(boost::asio::buffer(pod)); +} +template const void * pod2ptr(const T &pod){ + return boost::asio::buffer_cast(boost::asio::buffer(pod)); +} + +/*********************************************************************** + * Loopback runner: + * convert input buffer into intermediate buffer + * convert intermediate buffer into output buffer + **********************************************************************/ +template static void loopback( + size_t nsamps, const io_type_t &io_type, const otw_type_t &otw_type, - const host_type *input, - host_type *output + const Range &input, + Range &output ){ - dev_type dev[nsamps]; + //item32 is largest device type + std::vector dev(nsamps); //convert to dev type transport::convert_io_type_to_otw_type( - input, io_type, - dev, otw_type, + pod2ptr(input), io_type, + pod2ptr(dev), otw_type, nsamps ); //convert back to host type transport::convert_otw_type_to_io_type( - dev, otw_type, - output, io_type, + pod2ptr(dev), otw_type, + pod2ptr(output), io_type, nsamps ); } -typedef std::complex sc16_t; +/*********************************************************************** + * Test short conversion + **********************************************************************/ +static void test_convert_types_sc16( + size_t nsamps, + const io_type_t &io_type, + const otw_type_t &otw_type +){ + //fill the input samples + std::vector input(nsamps), output(nsamps); + BOOST_FOREACH(sc16_t &in, input) in = sc16_t( + std::rand()-(RAND_MAX/2), + std::rand()-(RAND_MAX/2) + ); -BOOST_AUTO_TEST_CASE(test_convert_types_be_sc16){ - sc16_t in_sc16[] = { - sc16_t(0, -1234), sc16_t(4321, 1234), - sc16_t(9876, -4567), sc16_t(8912, 0) - }, out_sc16[4]; + //run the loopback and test + loopback(nsamps, io_type, otw_type, input, output); + BOOST_CHECK_EQUAL_COLLECTIONS(input.begin(), input.end(), output.begin(), output.end()); +} +BOOST_AUTO_TEST_CASE(test_convert_types_be_sc16){ io_type_t io_type(io_type_t::COMPLEX_INT16); otw_type_t otw_type; otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; otw_type.width = 16; - loopback(io_type, otw_type, in_sc16, out_sc16); - BOOST_CHECK_EQUAL_COLLECTIONS(in_sc16, in_sc16+4, out_sc16, out_sc16+4); + //try various lengths to test edge cases + for (size_t nsamps = 0; nsamps < 16; nsamps++){ + test_convert_types_sc16(nsamps, io_type, otw_type); + } } BOOST_AUTO_TEST_CASE(test_convert_types_le_sc16){ - sc16_t in_sc16[] = { - sc16_t(0, -1234), sc16_t(4321, 1234), - sc16_t(9876, -4567), sc16_t(8912, 0) - }, out_sc16[4]; - io_type_t io_type(io_type_t::COMPLEX_INT16); otw_type_t otw_type; otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; otw_type.width = 16; - loopback(io_type, otw_type, in_sc16, out_sc16); - BOOST_CHECK_EQUAL_COLLECTIONS(in_sc16, in_sc16+4, out_sc16, out_sc16+4); + //try various lengths to test edge cases + for (size_t nsamps = 0; nsamps < 16; nsamps++){ + test_convert_types_sc16(nsamps, io_type, otw_type); + } } -typedef std::complex fc32_t; - -#define BOOST_CHECK_CLOSE_COMPLEX(a1, a2, p) \ - BOOST_CHECK_CLOSE(a1.real(), a2.real(), p); \ - BOOST_CHECK_CLOSE(a1.imag(), a2.imag(), p); +/*********************************************************************** + * Test float conversion + **********************************************************************/ +static void test_convert_types_fc32( + size_t nsamps, + const io_type_t &io_type, + const otw_type_t &otw_type +){ + //fill the input samples + std::vector input(nsamps), output(nsamps); + BOOST_FOREACH(fc32_t &in, input) in = fc32_t( + (std::rand()/float(RAND_MAX/2)) - 1, + (std::rand()/float(RAND_MAX/2)) - 1 + ); -static const float tolerance = float(0.1); + //run the loopback and test + loopback(nsamps, io_type, otw_type, input, output); + for (size_t i = 0; i < nsamps; i++){ + BOOST_CHECK_CLOSE_FRACTION(input[i].real(), output[i].real(), float(0.01)); + BOOST_CHECK_CLOSE_FRACTION(input[i].imag(), output[i].imag(), float(0.01)); + } +} BOOST_AUTO_TEST_CASE(test_convert_types_be_fc32){ - fc32_t in_fc32[] = { - fc32_t(float(0), float(-0.2)), fc32_t(float(0.03), float(-0.16)), - fc32_t(float(1.0), float(.45)), fc32_t(float(0.09), float(0)) - }, out_fc32[4]; - io_type_t io_type(io_type_t::COMPLEX_FLOAT32); otw_type_t otw_type; otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; otw_type.width = 16; - loopback(io_type, otw_type, in_fc32, out_fc32); - - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[0], out_fc32[0], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[1], out_fc32[1], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[2], out_fc32[2], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[3], out_fc32[3], tolerance); + //try various lengths to test edge cases + for (size_t nsamps = 0; nsamps < 16; nsamps++){ + test_convert_types_fc32(nsamps, io_type, otw_type); + } } BOOST_AUTO_TEST_CASE(test_convert_types_le_fc32){ - fc32_t in_fc32[] = { - fc32_t(float(0), float(-0.2)), fc32_t(float(0.03), float(-0.16)), - fc32_t(float(1.0), float(.45)), fc32_t(float(0.09), float(0)) - }, out_fc32[4]; - io_type_t io_type(io_type_t::COMPLEX_FLOAT32); otw_type_t otw_type; otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; otw_type.width = 16; - loopback(io_type, otw_type, in_fc32, out_fc32); - - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[0], out_fc32[0], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[1], out_fc32[1], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[2], out_fc32[2], tolerance); - BOOST_CHECK_CLOSE_COMPLEX(in_fc32[3], out_fc32[3], tolerance); + //try various lengths to test edge cases + for (size_t nsamps = 0; nsamps < 16; nsamps++){ + test_convert_types_fc32(nsamps, io_type, otw_type); + } } -- cgit v1.2.3 From 738f4a86558aca8d2fdfecd480613766bfc82510 Mon Sep 17 00:00:00 2001 From: Josh Blum Date: Wed, 25 Aug 2010 21:15:17 -0700 Subject: uhd: switched the IQ order interpretation for convert types --- host/lib/transport/convert_types_impl.hpp | 66 ++++++++++++++----------------- 1 file changed, 29 insertions(+), 37 deletions(-) (limited to 'host') diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp index 641029795..fdc859883 100644 --- a/host/lib/transport/convert_types_impl.hpp +++ b/host/lib/transport/convert_types_impl.hpp @@ -32,9 +32,6 @@ #include #endif -//! shortcut for a byteswap16 with casting -#define BSWAP16_C(num) uhd::byteswap(boost::uint16_t(num)) - /*********************************************************************** * Typedefs **********************************************************************/ @@ -54,10 +51,9 @@ static UHD_INLINE void sc16_to_item32_nswap( static UHD_INLINE void sc16_to_item32_bswap( const sc16_t *input, item32_t *output, size_t nsamps ){ + const item32_t *item32_input = (const item32_t *)input; for (size_t i = 0; i < nsamps; i++){ - boost::uint16_t real = BSWAP16_C(input[i].real()); - boost::uint16_t imag = BSWAP16_C(input[i].imag()); - output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + output[i] = uhd::byteswap(item32_input[i]); } } @@ -73,10 +69,9 @@ static UHD_INLINE void item32_to_sc16_nswap( static UHD_INLINE void item32_to_sc16_bswap( const item32_t *input, sc16_t *output, size_t nsamps ){ + item32_t *item32_output = (item32_t *)output; for (size_t i = 0; i < nsamps; i++){ - boost::int16_t real = BSWAP16_C(input[i] >> 0); - boost::int16_t imag = BSWAP16_C(input[i] >> 16); - output[i] = sc16_t(real, imag); + item32_output[i] = uhd::byteswap(input[i]); } } @@ -85,7 +80,11 @@ static UHD_INLINE void item32_to_sc16_bswap( **********************************************************************/ static const float shorts_per_float = float(32767); -#define FC32_TO_SC16_C(num) boost::int16_t(num*shorts_per_float) +static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ + boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); + boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); + return (item32_t(real) << 16) | (item32_t(imag) << 0); +} //////////////////////////////////// // none-swap @@ -106,8 +105,10 @@ static UHD_INLINE void fc32_to_item32_nswap( __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); - //pack + //pack + swap 16-bit pairs __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); //store to output _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); @@ -115,9 +116,7 @@ static UHD_INLINE void fc32_to_item32_nswap( //convert remainder for (; i < nsamps; i++){ - boost::uint16_t real = FC32_TO_SC16_C(input[i].real()); - boost::uint16_t imag = FC32_TO_SC16_C(input[i].imag()); - output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + output[i] = fc32_to_item32(input[i]); } } @@ -126,9 +125,7 @@ static UHD_INLINE void fc32_to_item32_nswap( const fc32_t *input, item32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - boost::uint16_t real = FC32_TO_SC16_C(input[i].real()); - boost::uint16_t imag = FC32_TO_SC16_C(input[i].imag()); - output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + output[i] = fc32_to_item32(input[i]); } } @@ -163,9 +160,7 @@ static UHD_INLINE void fc32_to_item32_bswap( //convert remainder for (; i < nsamps; i++){ - boost::uint16_t real = BSWAP16_C(FC32_TO_SC16_C(input[i].real())); - boost::uint16_t imag = BSWAP16_C(FC32_TO_SC16_C(input[i].imag())); - output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + output[i] = uhd::byteswap(fc32_to_item32(input[i])); } } @@ -174,9 +169,7 @@ static UHD_INLINE void fc32_to_item32_bswap( const fc32_t *input, item32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - boost::uint16_t real = BSWAP16_C(FC32_TO_SC16_C(input[i].real())); - boost::uint16_t imag = BSWAP16_C(FC32_TO_SC16_C(input[i].imag())); - output[i] = (item32_t(real) << 0) | (item32_t(imag) << 16); + output[i] = uhd::byteswap(fc32_to_item32(input[i])); } } @@ -187,7 +180,12 @@ static UHD_INLINE void fc32_to_item32_bswap( **********************************************************************/ static const float floats_per_short = float(1.0/shorts_per_float); -#define I16_TO_FC32_C(num) (boost::int16_t(num)*floats_per_short) +static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ + return fc32_t( + float(boost::int16_t(item >> 16)*floats_per_short), + float(boost::int16_t(item >> 0)*floats_per_short) + ); +} //////////////////////////////////// // none-swap @@ -204,7 +202,9 @@ static UHD_INLINE void item32_to_fc32_nswap( //load from input __m128i tmpi = _mm_loadu_si128(reinterpret_cast(input+i)); - //unpack + //unpack + swap 16-bit pairs + tmpi = _mm_shufflelo_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); + tmpi = _mm_shufflehi_epi16(tmpi, _MM_SHUFFLE(2, 3, 0, 1)); __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); @@ -219,9 +219,7 @@ static UHD_INLINE void item32_to_fc32_nswap( //convert remainder for (; i < nsamps; i++){ - float real = I16_TO_FC32_C(input[i] >> 0); - float imag = I16_TO_FC32_C(input[i] >> 16); - output[i] = fc32_t(real, imag); + output[i] = item32_to_fc32(input[i]); } } @@ -230,9 +228,7 @@ static UHD_INLINE void item32_to_fc32_nswap( const item32_t *input, fc32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - float real = I16_TO_FC32_C(input[i] >> 0); - float imag = I16_TO_FC32_C(input[i] >> 16); - output[i] = fc32_t(real, imag); + output[i] = item32_to_fc32(input[i]); } } #endif @@ -268,9 +264,7 @@ static UHD_INLINE void item32_to_fc32_bswap( //convert remainder for (; i < nsamps; i++){ - float real = I16_TO_FC32_C(BSWAP16_C(input[i] >> 0)); - float imag = I16_TO_FC32_C(BSWAP16_C(input[i] >> 16)); - output[i] = fc32_t(real, imag); + output[i] = item32_to_fc32(uhd::byteswap(input[i])); } } @@ -279,9 +273,7 @@ static UHD_INLINE void item32_to_fc32_bswap( const item32_t *input, fc32_t *output, size_t nsamps ){ for (size_t i = 0; i < nsamps; i++){ - float real = I16_TO_FC32_C(BSWAP16_C(input[i] >> 0)); - float imag = I16_TO_FC32_C(BSWAP16_C(input[i] >> 16)); - output[i] = fc32_t(real, imag); + output[i] = item32_to_fc32(uhd::byteswap(input[i])); } } -- cgit v1.2.3