Statistics
| Branch: | Tag: | Revision:

root / host / examples / rx_samples_to_file.cpp @ master

History | View | Annotate | Download (13.3 KB)

1
//
2
// Copyright 2010-2011 Ettus Research LLC
3
//
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

    
18
#include <uhd/utils/thread_priority.hpp>
19
#include <uhd/utils/safe_main.hpp>
20
#include <uhd/usrp/multi_usrp.hpp>
21
#include <uhd/exception.hpp>
22
#include <boost/program_options.hpp>
23
#include <boost/format.hpp>
24
#include <boost/thread.hpp>
25
#include <iostream>
26
#include <fstream>
27
#include <csignal>
28
#include <complex>
29

    
30
namespace po = boost::program_options;
31

    
32
static bool stop_signal_called = false;
33
void sig_int_handler(int){stop_signal_called = true;}
34

    
35
template<typename samp_type> void recv_to_file(
36
    uhd::usrp::multi_usrp::sptr usrp,
37
    const std::string &cpu_format,
38
    const std::string &wire_format,
39
    const std::string &file,
40
    size_t samps_per_buff,
41
    unsigned long long num_requested_samples,
42
    double time_requested = 0.0,
43
    bool bw_summary = false,
44
    bool stats = false,
45
    bool null = false,
46
    bool enable_size_map = false,
47
    bool continue_on_bad_packet = false
48
){
49
    unsigned long long num_total_samps = 0;
50
    //create a receive streamer
51
    uhd::stream_args_t stream_args(cpu_format,wire_format);
52
    uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args);
53

    
54
    uhd::rx_metadata_t md;
55
    std::vector<samp_type> buff(samps_per_buff);
56
    std::ofstream outfile;
57
    if (not null)
58
                outfile.open(file.c_str(), std::ofstream::binary);
59
    bool overflow_message = true;
60

    
61
    //setup streaming
62
    uhd::stream_cmd_t stream_cmd((num_requested_samples == 0)?
63
        uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS:
64
        uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE
65
    );
66
    stream_cmd.num_samps = num_requested_samples;
67
    stream_cmd.stream_now = true;
68
    stream_cmd.time_spec = uhd::time_spec_t();
69
    rx_stream->issue_stream_cmd(stream_cmd);
70
    
71
    boost::system_time start = boost::get_system_time();
72
    unsigned long long ticks_requested = (long)(time_requested * (double)boost::posix_time::time_duration::ticks_per_second());
73
    boost::posix_time::time_duration ticks_diff;
74
    boost::system_time last_update = start;
75
    unsigned long long last_update_samps = 0;
76
    
77
    typedef std::map<size_t,size_t> SizeMap;
78
    SizeMap mapSizes;
79

    
80
    while(not stop_signal_called and (num_requested_samples != num_total_samps or num_requested_samples == 0)){
81
                boost::system_time now = boost::get_system_time();
82
                
83
        size_t num_rx_samps = rx_stream->recv(&buff.front(), buff.size(), md, 3.0, enable_size_map);
84

    
85
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
86
            std::cout << boost::format("Timeout while streaming") << std::endl;
87
            break;
88
        }
89
        if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW){
90
            if (overflow_message){
91
                overflow_message = false;
92
                std::cerr << boost::format(
93
                    "Got an overflow indication. Please consider the following:\n"
94
                    "  Your write medium must sustain a rate of %fMB/s.\n"
95
                    "  Dropped samples will not be written to the file.\n"
96
                    "  Please modify this example for your purposes.\n"
97
                    "  This message will not appear again.\n"
98
                ) % (usrp->get_rx_rate()*sizeof(samp_type)/1e6);
99
            }
100
            continue;
101
        }
102
        if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE){
103
                        std::string error = str(boost::format(
104
                "Unexpected error code 0x%x"
105
            ) % md.error_code);
106
            
107
                        if (continue_on_bad_packet){
108
                                std::cerr << error << std::endl;
109
                                continue;
110
                        }
111
                        else
112
                                throw std::runtime_error(error);
113
        }
114
        
115
        if (enable_size_map){
116
                        SizeMap::iterator it = mapSizes.find(num_rx_samps);
117
                        if (it == mapSizes.end())
118
                                mapSizes[num_rx_samps] = 0;
119
                        mapSizes[num_rx_samps] += 1;
120
                }
121

    
122
        num_total_samps += num_rx_samps;
123

    
124
                if (outfile.is_open())
125
                        outfile.write((const char*)&buff.front(), num_rx_samps*sizeof(samp_type));
126

    
127
                if (bw_summary){
128
                        last_update_samps += num_rx_samps;
129
                        boost::posix_time::time_duration update_diff = now - last_update;
130
                        if (update_diff.ticks() > boost::posix_time::time_duration::ticks_per_second()) {
131
                                double t = (double)update_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second();
132
                                double r = (double)last_update_samps / t;
133
                                std::cout << boost::format("\t%f Msps") % (r/1e6) << std::endl;
134
                                last_update_samps = 0;
135
                                last_update = now;
136
                        }
137
                }
138
        
139
        ticks_diff = now - start;
140
                if (ticks_requested > 0){
141
                        if ((unsigned long long)ticks_diff.ticks() > ticks_requested)
142
                                break;
143
                }
144
    }
145
    
146
    if (outfile.is_open())
147
                outfile.close();
148
    
149
    if (stats){
150
                std::cout << std::endl;
151

    
152
                double t = (double)ticks_diff.ticks() / (double)boost::posix_time::time_duration::ticks_per_second();
153
                std::cout << boost::format("Received %d samples in %f seconds") % num_total_samps % t << std::endl;
154
                double r = (double)num_total_samps / t;
155
                std::cout << boost::format("%f Msps") % (r/1e6) << std::endl;
156
                
157
                if (enable_size_map) {
158
                        std::cout << std::endl;
159
                        std::cout << "Packet size map (bytes: count)" << std::endl;
160
                        for (SizeMap::iterator it = mapSizes.begin(); it != mapSizes.end(); it++)
161
                                std::cout << it->first << ":\t" << it->second << std::endl;
162
                }
163
        }
164
}
165

    
166
typedef boost::function<uhd::sensor_value_t (const std::string&)> get_sensor_fn_t;
167

    
168
bool check_locked_sensor(std::vector<std::string> sensor_names, const char* sensor_name, get_sensor_fn_t get_sensor_fn, double setup_time){
169
        if (std::find(sensor_names.begin(), sensor_names.end(), sensor_name) == sensor_names.end())
170
                return false;
171
        
172
        boost::system_time start = boost::get_system_time();
173
        boost::system_time first_lock_time;
174
        
175
        std::cout << boost::format("Waiting for \"%s\": ") % sensor_name;
176
        std::cout.flush();
177
        
178
        while (true){
179
                if ((not first_lock_time.is_not_a_date_time()) and
180
                        (boost::get_system_time() > (first_lock_time + boost::posix_time::seconds(setup_time))))
181
                {
182
                        std::cout << " locked." << std::endl;
183
                        break;
184
                }
185
                
186
                if (get_sensor_fn(sensor_name).to_bool()){
187
                        if (first_lock_time.is_not_a_date_time())
188
                                first_lock_time = boost::get_system_time();
189
                        std::cout << "+";
190
                        std::cout.flush();
191
                }
192
                else{
193
                        first_lock_time = boost::system_time();        //reset to 'not a date time'
194
                        
195
                        if (boost::get_system_time() > (start + boost::posix_time::seconds(setup_time))){
196
                                std::cout << std::endl;
197
                                throw std::runtime_error(str(boost::format("timed out waiting for consecutive locks on sensor \"%s\"") % sensor_name));
198
                        }
199
                        
200
                        std::cout << "_";
201
                        std::cout.flush();
202
                }
203
                
204
                boost::this_thread::sleep(boost::posix_time::milliseconds(100));
205
        }
206
        
207
        std::cout << std::endl;
208
        
209
        return true;
210
}
211

    
212
int UHD_SAFE_MAIN(int argc, char *argv[]){
213
    uhd::set_thread_priority_safe();
214

    
215
    //variables to be set by po
216
    std::string args, file, type, ant, subdev, ref, wirefmt;
217
    size_t total_num_samps, spb;
218
    double rate, freq, gain, bw, total_time, setup_time;
219

    
220
    //setup the program options
221
    po::options_description desc("Allowed options");
222
    desc.add_options()
223
        ("help", "help message")
224
        ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args")
225
        ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to write binary samples to")
226
        ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short")
227
        ("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive")
228
        ("time", po::value<double>(&total_time)->default_value(0), "total number of seconds to receive")
229
        ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer")
230
        ("rate", po::value<double>(&rate)->default_value(1e6), "rate of incoming samples")
231
        ("freq", po::value<double>(&freq)->default_value(0.0), "RF center frequency in Hz")
232
        ("gain", po::value<double>(&gain), "gain for the RF chain")
233
        ("ant", po::value<std::string>(&ant), "daughterboard antenna selection")
234
        ("subdev", po::value<std::string>(&subdev), "daughterboard subdevice specification")
235
        ("bw", po::value<double>(&bw), "daughterboard IF filter bandwidth in Hz")
236
        ("ref", po::value<std::string>(&ref)->default_value("internal"), "waveform type (internal, external, mimo)")
237
        ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)")
238
        ("setup", po::value<double>(&setup_time)->default_value(1.0), "seconds of setup time")
239
        ("progress", "show average bandwidth on exit")
240
        ("stats", "periodically display short-term bandwidth")
241
        ("sizemap", "track packet size and display breakdown on exit")
242
        ("null", "run without writing to file")
243
        ("continue", "don't abort on a bad packet")
244
        ("skip-lo", "skip checking LO lock status")
245
    ;
246
    po::variables_map vm;
247
    po::store(po::parse_command_line(argc, argv, desc), vm);
248
    po::notify(vm);
249

    
250
    //print the help message
251
    if (vm.count("help")){
252
        std::cout << boost::format("UHD RX samples to file %s") % desc << std::endl;
253
        return ~0;
254
    }
255
    
256
    bool bw_summary = vm.count("progress") > 0;
257
    bool stats = vm.count("stats") > 0;
258
    bool null = vm.count("null") > 0;
259
    bool enable_size_map = vm.count("sizemap") > 0;
260
    bool continue_on_bad_packet = vm.count("continue") > 0;
261
    
262
    if (enable_size_map)
263
                std::cout << "Packet size tracking enabled - will only recv one packet at a time!" << std::endl;
264

    
265
    //create a usrp device
266
    std::cout << std::endl;
267
    std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl;
268
    uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
269

    
270
    //Lock mboard clocks
271
    usrp->set_clock_source(ref);
272

    
273
    //always select the subdevice first, the channel mapping affects the other settings
274
    if (vm.count("subdev")) usrp->set_rx_subdev_spec(subdev);
275

    
276
    std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
277

    
278
    //set the sample rate
279
    if (rate <= 0.0){
280
        std::cerr << "Please specify a valid sample rate" << std::endl;
281
        return ~0;
282
    }
283
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate/1e6) << std::endl;
284
    usrp->set_rx_rate(rate);
285
    std::cout << boost::format("Actual RX Rate: %f Msps...") % (usrp->get_rx_rate()/1e6) << std::endl << std::endl;
286

    
287
    //set the center frequency
288
    if (vm.count("freq")){        //with default of 0.0 this will always be true
289
                std::cout << boost::format("Setting RX Freq: %f MHz...") % (freq/1e6) << std::endl;
290
                usrp->set_rx_freq(freq);
291
                std::cout << boost::format("Actual RX Freq: %f MHz...") % (usrp->get_rx_freq()/1e6) << std::endl << std::endl;
292
        }
293

    
294
    //set the rf gain
295
    if (vm.count("gain")){
296
        std::cout << boost::format("Setting RX Gain: %f dB...") % gain << std::endl;
297
        usrp->set_rx_gain(gain);
298
        std::cout << boost::format("Actual RX Gain: %f dB...") % usrp->get_rx_gain() << std::endl << std::endl;
299
    }
300

    
301
    //set the IF filter bandwidth
302
    if (vm.count("bw")){
303
        std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % bw << std::endl;
304
        usrp->set_rx_bandwidth(bw);
305
        std::cout << boost::format("Actual RX Bandwidth: %f MHz...") % usrp->get_rx_bandwidth() << std::endl << std::endl;
306
    }
307

    
308
    //set the antenna
309
    if (vm.count("ant")) usrp->set_rx_antenna(ant);
310

    
311
    boost::this_thread::sleep(boost::posix_time::seconds(setup_time)); //allow for some setup time
312

    
313
    //check Ref and LO Lock detect
314
    if (not vm.count("skip-lo")){
315
                check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked", boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, 0), setup_time);
316
                if (ref == "mimo")
317
                        check_locked_sensor(usrp->get_mboard_sensor_names(0), "mimo_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time);
318
                if (ref == "external")
319
                        check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked", boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, 0), setup_time);
320
        }
321

    
322
    if (total_num_samps == 0){
323
        std::signal(SIGINT, &sig_int_handler);
324
        std::cout << "Press Ctrl + C to stop streaming..." << std::endl;
325
    }
326

    
327
#define recv_to_file_args(format) \
328
        (usrp, format, wirefmt, file, spb, total_num_samps, total_time, bw_summary, stats, null, enable_size_map, continue_on_bad_packet)
329
    //recv to file
330
    if (type == "double") recv_to_file<std::complex<double> >recv_to_file_args("fc64");
331
    else if (type == "float") recv_to_file<std::complex<float> >recv_to_file_args("fc32");
332
    else if (type == "short") recv_to_file<std::complex<short> >recv_to_file_args("sc16");
333
    else throw std::runtime_error("Unknown type " + type);
334

    
335
    //finished
336
    std::cout << std::endl << "Done!" << std::endl << std::endl;
337

    
338
    return EXIT_SUCCESS;
339
}