Revision 6798b4f8 host/examples/tx_waveforms.cpp
| b/host/examples/tx_waveforms.cpp | ||
|---|---|---|
| 22 | 22 |
#include <boost/thread/thread_time.hpp> //system time |
| 23 | 23 |
#include <boost/math/special_functions/round.hpp> |
| 24 | 24 |
#include <boost/format.hpp> |
| 25 |
#include <boost/function.hpp> |
|
| 25 | 26 |
#include <iostream> |
| 26 | 27 |
#include <complex> |
| 27 | 28 |
#include <cmath> |
| 28 | 29 |
|
| 29 | 30 |
namespace po = boost::program_options; |
| 30 | 31 |
|
| 32 |
/*********************************************************************** |
|
| 33 |
* Waveform generators |
|
| 34 |
**********************************************************************/ |
|
| 35 |
float gen_const(float){
|
|
| 36 |
return 1; |
|
| 37 |
} |
|
| 38 |
|
|
| 39 |
float gen_square(float x){
|
|
| 40 |
return (std::fmod(x, 1) < float(0.5))? 0 : 1; |
|
| 41 |
} |
|
| 42 |
|
|
| 43 |
float gen_ramp(float x){
|
|
| 44 |
return std::fmod(x, 1)*2 - 1; |
|
| 45 |
} |
|
| 46 |
|
|
| 47 |
float gen_sine(float x){
|
|
| 48 |
return std::sin(x*2*M_PI); |
|
| 49 |
} |
|
| 50 |
|
|
| 31 | 51 |
int UHD_SAFE_MAIN(int argc, char *argv[]){
|
| 32 | 52 |
uhd::set_thread_priority_safe(); |
| 33 | 53 |
|
| 34 | 54 |
//variables to be set by po |
| 35 | 55 |
std::string args, wave_type; |
| 36 |
size_t total_duration, mspb;
|
|
| 37 |
double rate, freq, wave_freq, aepb;
|
|
| 56 |
size_t total_duration, spb; |
|
| 57 |
double rate, freq, wave_freq; |
|
| 38 | 58 |
float ampl, gain; |
| 39 | 59 |
|
| 40 | 60 |
//setup the program options |
| ... | ... | |
| 43 | 63 |
("help", "help message")
|
| 44 | 64 |
("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args")
|
| 45 | 65 |
("duration", po::value<size_t>(&total_duration)->default_value(3), "number of seconds to transmit")
|
| 46 |
("mspb", po::value<size_t>(&mspb)->default_value(10000), "mimimum samples per buffer")
|
|
| 47 |
("aepb", po::value<double>(&aepb)->default_value(1e-5), "allowed error per buffer")
|
|
| 66 |
("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer")
|
|
| 48 | 67 |
("rate", po::value<double>(&rate)->default_value(100e6/16), "rate of outgoing samples")
|
| 49 | 68 |
("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz")
|
| 50 | 69 |
("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of the waveform")
|
| ... | ... | |
| 90 | 109 |
} |
| 91 | 110 |
|
| 92 | 111 |
//error when the waveform is not possible to generate |
| 93 |
if (std::abs(wave_freq)/sdev->get_tx_rate() < 0.5/mspb){
|
|
| 94 |
throw std::runtime_error("wave freq/tx rate too small");
|
|
| 95 |
} |
|
| 96 | 112 |
if (std::abs(wave_freq) > sdev->get_tx_rate()/2){
|
| 97 | 113 |
throw std::runtime_error("wave freq out of Nyquist zone");
|
| 98 | 114 |
} |
| 99 | 115 |
|
| 100 |
//how many periods should we have per buffer to mimimize error |
|
| 101 |
double samps_per_period = sdev->get_tx_rate()/std::abs(wave_freq); |
|
| 102 |
std::cout << boost::format("Samples per waveform period: %d") % samps_per_period << std::endl;
|
|
| 103 |
size_t periods_per_buff = std::max<size_t>(1, mspb/samps_per_period); |
|
| 104 |
while (true){
|
|
| 105 |
double num_samps_per_buff = periods_per_buff*samps_per_period; |
|
| 106 |
double sample_error = num_samps_per_buff - boost::math::round(num_samps_per_buff); |
|
| 107 |
if (std::abs(sample_error) <= aepb) break; |
|
| 108 |
periods_per_buff++; |
|
| 109 |
} |
|
| 110 |
|
|
| 111 |
//allocate data to send (fill with several periods worth of IQ samples) |
|
| 112 |
std::vector<std::complex<float> > buff(samps_per_period*periods_per_buff); |
|
| 113 |
const double i_ahead = (wave_freq > 0)? samps_per_period/4 : 0; |
|
| 114 |
const double q_ahead = (wave_freq < 0)? samps_per_period/4 : 0; |
|
| 115 |
std::cout << boost::format("Samples per send buffer: %d") % buff.size() << std::endl;
|
|
| 116 |
if (wave_type == "CONST"){
|
|
| 117 |
for (size_t n = 0; n < buff.size(); n++){
|
|
| 118 |
buff[n] = std::complex<float>(ampl, ampl); |
|
| 119 |
} |
|
| 120 |
} |
|
| 121 |
else if (wave_type == "SQUARE"){
|
|
| 122 |
for (size_t n = 0; n < buff.size(); n++){
|
|
| 123 |
float I = (std::fmod(n+i_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; |
|
| 124 |
float Q = (std::fmod(n+q_ahead, samps_per_period) > samps_per_period/2)? ampl : 0; |
|
| 125 |
buff[n] = std::complex<float>(I, Q); |
|
| 126 |
} |
|
| 127 |
} |
|
| 128 |
else if (wave_type == "RAMP"){
|
|
| 129 |
for (size_t n = 0; n < buff.size(); n++){
|
|
| 130 |
float I = float(std::fmod(n+i_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); |
|
| 131 |
float Q = float(std::fmod(n+q_ahead, samps_per_period)/samps_per_period * 2*ampl - ampl); |
|
| 132 |
buff[n] = std::complex<float>(I, Q); |
|
| 133 |
} |
|
| 134 |
} |
|
| 135 |
else if (wave_type == "SINE"){
|
|
| 136 |
for (size_t n = 0; n < buff.size(); n++){
|
|
| 137 |
float I = float(ampl*std::sin(2*M_PI*(n+i_ahead)/samps_per_period)); |
|
| 138 |
float Q = float(ampl*std::sin(2*M_PI*(n+q_ahead)/samps_per_period)); |
|
| 139 |
buff[n] = std::complex<float>(I, Q); |
|
| 140 |
} |
|
| 141 |
} |
|
| 116 |
//store the generator function for the selected waveform |
|
| 117 |
boost::function<float(float)> wave_gen; |
|
| 118 |
if (wave_type == "CONST") wave_gen = &gen_const; |
|
| 119 |
else if (wave_type == "SQUARE") wave_gen = &gen_square; |
|
| 120 |
else if (wave_type == "RAMP") wave_gen = &gen_ramp; |
|
| 121 |
else if (wave_type == "SINE") wave_gen = &gen_sine; |
|
| 142 | 122 |
else throw std::runtime_error("unknown waveform type: " + wave_type);
|
| 143 | 123 |
|
| 124 |
//allocate the buffer and precalculate values |
|
| 125 |
std::vector<std::complex<float> > buff(spb); |
|
| 126 |
const float cps = wave_freq/sdev->get_tx_rate(); |
|
| 127 |
const float i_off = (wave_freq > 0)? float(0.25) : 0; |
|
| 128 |
const float q_off = (wave_freq < 0)? float(0.25) : 0; |
|
| 129 |
float theta = 0; |
|
| 130 |
|
|
| 144 | 131 |
//setup the metadata flags |
| 145 | 132 |
uhd::tx_metadata_t md; |
| 146 | 133 |
md.start_of_burst = true; //always SOB (good for continuous streaming) |
| ... | ... | |
| 148 | 135 |
|
| 149 | 136 |
//send the data in multiple packets |
| 150 | 137 |
boost::system_time end_time(boost::get_system_time() + boost::posix_time::seconds(total_duration)); |
| 151 |
while(end_time > boost::get_system_time()) dev->send( |
|
| 152 |
&buff.front(), buff.size(), md, |
|
| 153 |
uhd::io_type_t::COMPLEX_FLOAT32, |
|
| 154 |
uhd::device::SEND_MODE_FULL_BUFF |
|
| 155 |
); |
|
| 138 |
while(end_time > boost::get_system_time()){
|
|
| 139 |
|
|
| 140 |
//fill the buffer with the waveform |
|
| 141 |
for (size_t n = 0; n < buff.size(); n++){
|
|
| 142 |
buff[n] = std::complex<float>( |
|
| 143 |
ampl*wave_gen(i_off + theta), |
|
| 144 |
ampl*wave_gen(q_off + theta) |
|
| 145 |
); |
|
| 146 |
theta += cps; |
|
| 147 |
} |
|
| 148 |
|
|
| 149 |
//bring the theta back into range [0, 1) |
|
| 150 |
theta = std::fmod(theta, 1); |
|
| 151 |
|
|
| 152 |
//send the entire contents of the buffer |
|
| 153 |
dev->send( |
|
| 154 |
&buff.front(), buff.size(), md, |
|
| 155 |
uhd::io_type_t::COMPLEX_FLOAT32, |
|
| 156 |
uhd::device::SEND_MODE_FULL_BUFF |
|
| 157 |
); |
|
| 158 |
} |
|
| 156 | 159 |
|
| 157 | 160 |
//send a mini EOB packet |
| 158 | 161 |
md.start_of_burst = false; |
Also available in: Unified diff