1 /* -*- c++ -*- */
2 /*
3 * Copyright 2012 Dimitri Stolnikov <horiz0n@gmx.net>
4 *
5 * GNU Radio is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3, or (at your option)
8 * any later version.
9 *
10 * GNU Radio is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with GNU Radio; see the file COPYING. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 /*
22 * config.h is generated by configure. It contains the results
23 * of probing for features, options etc. It should be the first
24 * file included in your .cc file.
25 */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "rtl_source_c.h"
31 #include <gnuradio/io_signature.h>
32
33 #include <boost/assign.hpp>
34 #include <boost/format.hpp>
35 #include <boost/algorithm/string.hpp>
36
37 #include <stdexcept>
38 #include <iostream>
39 #include <stdio.h>
40
41 #include <rtl-sdr.h>
42
43 #include "arg_helpers.h"
44
45 using namespace boost::assign;
46
47 #define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
48 #define BUF_NUM 15
49 #define BUF_SKIP 1 // buffers to skip due to initial garbage
50
51 #define BYTES_PER_SAMPLE 2 // rtl device delivers 8 bit unsigned IQ data
52
53 /*
54 * Create a new instance of rtl_source_c and return
55 * a boost shared_ptr. This is effectively the public constructor.
56 */
57 rtl_source_c_sptr
make_rtl_source_c(const std::string & args)58 make_rtl_source_c (const std::string &args)
59 {
60 return gnuradio::get_initial_sptr(new rtl_source_c (args));
61 }
62
63 /*
64 * Specify constraints on number of input and output streams.
65 * This info is used to construct the input and output signatures
66 * (2nd & 3rd args to gr::block's constructor). The input and
67 * output signatures are used by the runtime system to
68 * check that a valid number and type of inputs and outputs
69 * are connected to this block. In this case, we accept
70 * only 0 input and 1 output.
71 */
72 static const int MIN_IN = 0; // mininum number of input streams
73 static const int MAX_IN = 0; // maximum number of input streams
74 static const int MIN_OUT = 1; // minimum number of output streams
75 static const int MAX_OUT = 1; // maximum number of output streams
76
77 /*
78 * The private constructor
79 */
rtl_source_c(const std::string & args)80 rtl_source_c::rtl_source_c (const std::string &args)
81 : gr::sync_block ("rtl_source_c",
82 gr::io_signature::make(MIN_IN, MAX_IN, sizeof (gr_complex)),
83 gr::io_signature::make(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
84 _dev(NULL),
85 _buf(NULL),
86 _running(false),
87 _no_tuner(false),
88 _auto_gain(false),
89 _if_gain(0),
90 _skipped(0)
91 {
92 int ret;
93 int index;
94 int bias_tee = 0;
95 unsigned int dev_index = 0, rtl_freq = 0, tuner_freq = 0, direct_samp = 0;
96 unsigned int offset_tune = 0;
97 char manufact[256];
98 char product[256];
99 char serial[256];
100
101 dict_t dict = params_to_dict(args);
102
103 if (dict.count("rtl")) {
104 std::string value = dict["rtl"];
105
106 if ( (index = rtlsdr_get_index_by_serial( value.c_str() )) >= 0 ) {
107 dev_index = index; /* use the resolved index value */
108 } else { /* use the numeric value of the argument */
109 if ( value.length() ) {
110 try {
111 dev_index = boost::lexical_cast< unsigned int >( value );
112 } catch ( std::exception &ex ) {
113 throw std::runtime_error(
114 "Failed to use '" + value + "' as index: " + ex.what());
115 }
116 }
117 }
118 }
119
120 if ( dev_index >= rtlsdr_get_device_count() )
121 throw std::runtime_error("Wrong rtlsdr device index given.");
122
123 std::cerr << "Using device #" << dev_index;
124
125 memset(manufact, 0, sizeof(manufact));
126 memset(product, 0, sizeof(product));
127 memset(serial, 0, sizeof(serial));
128 if ( !rtlsdr_get_device_usb_strings( dev_index, manufact, product, serial ) ) {
129 if (strlen(manufact))
130 std::cerr << " " << manufact;
131 if (strlen(product))
132 std::cerr << " " << product;
133 if (strlen(serial))
134 std::cerr << " SN: " << serial;
135 } else {
136 std::cerr << " " << rtlsdr_get_device_name(dev_index);
137 }
138
139 std::cerr << std::endl;
140
141 if (dict.count("rtl_xtal"))
142 rtl_freq = (unsigned int)boost::lexical_cast< double >( dict["rtl_xtal"] );
143
144 if (dict.count("tuner_xtal"))
145 tuner_freq = (unsigned int)boost::lexical_cast< double >( dict["tuner_xtal"] );
146
147 if (dict.count("direct_samp"))
148 direct_samp = boost::lexical_cast< unsigned int >( dict["direct_samp"] );
149
150 if (dict.count("offset_tune"))
151 offset_tune = boost::lexical_cast< unsigned int >( dict["offset_tune"] );
152
153 if (dict.count("bias"))
154 bias_tee = boost::lexical_cast<bool>( dict["bias"] );
155
156 _buf_num = _buf_len = _buf_head = _buf_used = _buf_offset = 0;
157
158 if (dict.count("buffers"))
159 _buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] );
160
161 if (dict.count("buflen"))
162 _buf_len = boost::lexical_cast< unsigned int >( dict["buflen"] );
163
164 if (0 == _buf_num)
165 _buf_num = BUF_NUM;
166
167 if (0 == _buf_len || _buf_len % 512 != 0) /* len must be multiple of 512 */
168 _buf_len = BUF_LEN;
169
170 if ( BUF_NUM != _buf_num || BUF_LEN != _buf_len ) {
171 std::cerr << "Using " << _buf_num << " buffers of size " << _buf_len << "."
172 << std::endl;
173 }
174
175 _samp_avail = _buf_len / BYTES_PER_SAMPLE;
176
177 // create a lookup table for gr_complex values
178 for (unsigned int i = 0; i < 0x100; i++)
179 _lut.push_back((i - 127.4f) / 128.0f);
180
181 _dev = NULL;
182 ret = rtlsdr_open( &_dev, dev_index );
183 if (ret < 0)
184 throw std::runtime_error("Failed to open rtlsdr device.");
185
186 if (rtl_freq > 0 || tuner_freq > 0) {
187 if (rtl_freq)
188 std::cerr << "Setting rtl clock to " << rtl_freq << " Hz." << std::endl;
189 if (tuner_freq)
190 std::cerr << "Setting tuner clock to " << tuner_freq << " Hz." << std::endl;
191
192 ret = rtlsdr_set_xtal_freq( _dev, rtl_freq, tuner_freq );
193 if (ret < 0)
194 throw std::runtime_error(
195 str(boost::format("Failed to set xtal frequencies. Error %d.") % ret ));
196 }
197
198 ret = rtlsdr_set_sample_rate( _dev, 1024000 );
199 if (ret < 0)
200 throw std::runtime_error("Failed to set default samplerate.");
201
202 ret = rtlsdr_set_tuner_gain_mode(_dev, int(!_auto_gain));
203 if (ret < 0)
204 throw std::runtime_error("Failed to set tuner gain mode.");
205
206 ret = rtlsdr_set_agc_mode(_dev, int(_auto_gain));
207 if (ret < 0)
208 throw std::runtime_error("Failed to set agc mode.");
209
210 if (direct_samp) {
211 ret = rtlsdr_set_direct_sampling(_dev, direct_samp);
212 if (ret < 0)
213 throw std::runtime_error("Failed to enable direct sampling.");
214 _no_tuner = true;
215 }
216
217 if (offset_tune) {
218 ret = rtlsdr_set_offset_tuning(_dev, offset_tune);
219 if (ret < 0)
220 throw std::runtime_error("Failed to enable offset tuning.");
221 }
222
223 ret = rtlsdr_set_bias_tee(_dev, bias_tee);
224 if (ret < 0)
225 throw std::runtime_error("Failed to set bias tee.");
226
227 ret = rtlsdr_reset_buffer( _dev );
228 if (ret < 0)
229 throw std::runtime_error("Failed to reset usb buffers.");
230
231 set_if_gain( 24 ); /* preset to a reasonable default (non-GRC use case) */
232
233 _buf = (unsigned char **)malloc(_buf_num * sizeof(unsigned char *));
234
235 if (_buf) {
236 for(unsigned int i = 0; i < _buf_num; ++i)
237 _buf[i] = (unsigned char *)malloc(_buf_len);
238 }
239 }
240
241 /*
242 * Our virtual destructor.
243 */
~rtl_source_c()244 rtl_source_c::~rtl_source_c ()
245 {
246 if (_dev) {
247 if (_running)
248 {
249 _running = false;
250 rtlsdr_cancel_async( _dev );
251 _thread.join();
252 }
253
254 rtlsdr_close( _dev );
255 _dev = NULL;
256 }
257
258 if (_buf) {
259 for(unsigned int i = 0; i < _buf_num; ++i) {
260 free(_buf[i]);
261 }
262
263 free(_buf);
264 _buf = NULL;
265 }
266 }
267
start()268 bool rtl_source_c::start()
269 {
270 _running = true;
271 _thread = gr::thread::thread(_rtlsdr_wait, this);
272
273 return true;
274 }
275
stop()276 bool rtl_source_c::stop()
277 {
278 _running = false;
279 if (_dev)
280 rtlsdr_cancel_async( _dev );
281 _thread.join();
282
283 return true;
284 }
285
_rtlsdr_callback(unsigned char * buf,uint32_t len,void * ctx)286 void rtl_source_c::_rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
287 {
288 rtl_source_c *obj = (rtl_source_c *)ctx;
289 obj->rtlsdr_callback(buf, len);
290 }
291
rtlsdr_callback(unsigned char * buf,uint32_t len)292 void rtl_source_c::rtlsdr_callback(unsigned char *buf, uint32_t len)
293 {
294 if (_skipped < BUF_SKIP) {
295 _skipped++;
296 return;
297 }
298
299 {
300 std::lock_guard<std::mutex> lock( _buf_mutex );
301
302 int buf_tail = (_buf_head + _buf_used) % _buf_num;
303 memcpy(_buf[buf_tail], buf, len);
304
305 if (_buf_used == _buf_num) {
306 std::cerr << "O" << std::flush;
307 _buf_head = (_buf_head + 1) % _buf_num;
308 } else {
309 _buf_used++;
310 }
311 }
312
313 _buf_cond.notify_one();
314 }
315
_rtlsdr_wait(rtl_source_c * obj)316 void rtl_source_c::_rtlsdr_wait(rtl_source_c *obj)
317 {
318 obj->rtlsdr_wait();
319 }
320
rtlsdr_wait()321 void rtl_source_c::rtlsdr_wait()
322 {
323 int ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, _buf_num, _buf_len );
324
325 _running = false;
326
327 if ( ret != 0 )
328 std::cerr << "rtlsdr_read_async returned with " << ret << std::endl;
329
330 _buf_cond.notify_one();
331 }
332
work(int noutput_items,gr_vector_const_void_star & input_items,gr_vector_void_star & output_items)333 int rtl_source_c::work( int noutput_items,
334 gr_vector_const_void_star &input_items,
335 gr_vector_void_star &output_items )
336 {
337 gr_complex *out = (gr_complex *)output_items[0];
338
339 {
340 std::unique_lock<std::mutex> lock( _buf_mutex );
341
342 while (_buf_used < 3 && _running) // collect at least 3 buffers
343 _buf_cond.wait( lock );
344 }
345
346 if (!_running)
347 return WORK_DONE;
348
349 while (noutput_items && _buf_used) {
350 const int nout = std::min(noutput_items, _samp_avail);
351 const unsigned char *buf = _buf[_buf_head] + _buf_offset * 2;
352
353 for (int i = 0; i < nout; ++i)
354 *out++ = gr_complex(_lut[buf[i * 2]], _lut[buf[i * 2 + 1]]);
355
356 noutput_items -= nout;
357 _samp_avail -= nout;
358
359 if (!_samp_avail) {
360 {
361 std::lock_guard<std::mutex> lock( _buf_mutex );
362
363 _buf_head = (_buf_head + 1) % _buf_num;
364 _buf_used--;
365 }
366 _samp_avail = _buf_len / BYTES_PER_SAMPLE;
367 _buf_offset = 0;
368 } else {
369 _buf_offset += nout;
370 }
371 }
372
373 return (out - ((gr_complex *)output_items[0]));
374 }
375
get_devices()376 std::vector<std::string> rtl_source_c::get_devices()
377 {
378 std::vector<std::string> devices;
379 std::string label;
380 char manufact[256];
381 char product[256];
382 char serial[256];
383
384 for (unsigned int i = 0; i < rtlsdr_get_device_count(); i++) {
385 std::string args = "rtl=" + boost::lexical_cast< std::string >( i );
386
387 label.clear();
388
389 memset(manufact, 0, sizeof(manufact));
390 memset(product, 0, sizeof(product));
391 memset(serial, 0, sizeof(serial));
392 if ( !rtlsdr_get_device_usb_strings( i, manufact, product, serial ) ) {
393 if (strlen(manufact))
394 label += std::string(manufact) + " ";
395 if (strlen(product))
396 label += std::string(product) + " ";
397 if (strlen(serial))
398 label += "SN: " + std::string(serial) + " ";
399 } else {
400 label = std::string(rtlsdr_get_device_name(i));
401 }
402
403 boost::algorithm::trim(label);
404
405 args += ",label='" + label + "'";
406 devices.push_back( args );
407 }
408
409 return devices;
410 }
411
get_num_channels()412 size_t rtl_source_c::get_num_channels()
413 {
414 return 1;
415 }
416
get_sample_rates()417 osmosdr::meta_range_t rtl_source_c::get_sample_rates()
418 {
419 osmosdr::meta_range_t range;
420
421 range += osmosdr::range_t( 250000 ); // known to work
422 range += osmosdr::range_t( 1000000 ); // known to work
423 range += osmosdr::range_t( 1024000 ); // known to work
424 range += osmosdr::range_t( 1800000 ); // known to work
425 range += osmosdr::range_t( 1920000 ); // known to work
426 range += osmosdr::range_t( 2000000 ); // known to work
427 range += osmosdr::range_t( 2048000 ); // known to work
428 range += osmosdr::range_t( 2400000 ); // known to work
429 range += osmosdr::range_t( 2560000 ); // known to work
430 // range += osmosdr::range_t( 2600000 ); // may work
431 // range += osmosdr::range_t( 2800000 ); // may work
432 // range += osmosdr::range_t( 3000000 ); // may work
433 // range += osmosdr::range_t( 3200000 ); // max rate
434
435 return range;
436 }
437
set_sample_rate(double rate)438 double rtl_source_c::set_sample_rate(double rate)
439 {
440 if (_dev) {
441 rtlsdr_set_sample_rate( _dev, (uint32_t)rate );
442 }
443
444 return get_sample_rate();
445 }
446
get_sample_rate()447 double rtl_source_c::get_sample_rate()
448 {
449 if (_dev)
450 return (double)rtlsdr_get_sample_rate( _dev );
451
452 return 0;
453 }
454
get_freq_range(size_t chan)455 osmosdr::freq_range_t rtl_source_c::get_freq_range( size_t chan )
456 {
457 osmosdr::freq_range_t range;
458
459 if (_dev) {
460 if (_no_tuner) {
461 uint32_t rtl_freq;
462 if ( !rtlsdr_get_xtal_freq( _dev, &rtl_freq, NULL ) )
463 range += osmosdr::range_t( 0, double(rtl_freq) );
464 return range;
465 }
466
467 enum rtlsdr_tuner tuner = rtlsdr_get_tuner_type(_dev);
468
469 if ( tuner == RTLSDR_TUNER_E4000 ) {
470 /* there is a (temperature dependent) gap between 1100 to 1250 MHz */
471 range += osmosdr::range_t( 52e6, 2.2e9 );
472 } else if ( tuner == RTLSDR_TUNER_FC0012 ) {
473 range += osmosdr::range_t( 22e6, 948e6 );
474 } else if ( tuner == RTLSDR_TUNER_FC0013 ) {
475 range += osmosdr::range_t( 22e6, 1.1e9 );
476 } else if ( tuner == RTLSDR_TUNER_FC2580 ) {
477 range += osmosdr::range_t( 146e6, 308e6 );
478 range += osmosdr::range_t( 438e6, 924e6 );
479 } else if ( tuner == RTLSDR_TUNER_R820T ) {
480 range += osmosdr::range_t( 24e6, 1766e6 );
481 } else if ( tuner == RTLSDR_TUNER_R828D ) {
482 range += osmosdr::range_t( 24e6, 1766e6 );
483 }
484 }
485
486 return range;
487 }
488
set_center_freq(double freq,size_t chan)489 double rtl_source_c::set_center_freq( double freq, size_t chan )
490 {
491 if (_dev)
492 rtlsdr_set_center_freq( _dev, (uint32_t)freq );
493
494 return get_center_freq( chan );
495 }
496
get_center_freq(size_t chan)497 double rtl_source_c::get_center_freq( size_t chan )
498 {
499 if (_dev)
500 return (double)rtlsdr_get_center_freq( _dev );
501
502 return 0;
503 }
504
set_freq_corr(double ppm,size_t chan)505 double rtl_source_c::set_freq_corr( double ppm, size_t chan )
506 {
507 if ( _dev )
508 rtlsdr_set_freq_correction( _dev, (int)ppm );
509
510 return get_freq_corr( chan );
511 }
512
get_freq_corr(size_t chan)513 double rtl_source_c::get_freq_corr( size_t chan )
514 {
515 if ( _dev )
516 return (double)rtlsdr_get_freq_correction( _dev );
517
518 return 0;
519 }
520
get_gain_names(size_t chan)521 std::vector<std::string> rtl_source_c::get_gain_names( size_t chan )
522 {
523 std::vector< std::string > names;
524
525 names += "LNA";
526
527 if ( _dev ) {
528 if ( rtlsdr_get_tuner_type(_dev) == RTLSDR_TUNER_E4000 ) {
529 names += "IF";
530 }
531 }
532
533 return names;
534 }
535
get_gain_range(size_t chan)536 osmosdr::gain_range_t rtl_source_c::get_gain_range( size_t chan )
537 {
538 osmosdr::gain_range_t range;
539
540 if (_dev) {
541 int count = rtlsdr_get_tuner_gains(_dev, NULL);
542 if (count > 0) {
543 int* gains = new int[ count ];
544 count = rtlsdr_get_tuner_gains(_dev, gains);
545 for (int i = 0; i < count; i++)
546 range += osmosdr::range_t( gains[i] / 10.0 );
547 delete[] gains;
548 }
549 }
550
551 return range;
552 }
553
get_gain_range(const std::string & name,size_t chan)554 osmosdr::gain_range_t rtl_source_c::get_gain_range( const std::string & name, size_t chan )
555 {
556 if ( "IF" == name ) {
557 if ( _dev ) {
558 if ( rtlsdr_get_tuner_type(_dev) == RTLSDR_TUNER_E4000 ) {
559 return osmosdr::gain_range_t(3, 56, 1);
560 } else {
561 return osmosdr::gain_range_t();
562 }
563 }
564 }
565
566 return get_gain_range( chan );
567 }
568
set_gain_mode(bool automatic,size_t chan)569 bool rtl_source_c::set_gain_mode( bool automatic, size_t chan )
570 {
571 if (_dev) {
572 if (!rtlsdr_set_tuner_gain_mode(_dev, int(!automatic))) {
573 _auto_gain = automatic;
574 }
575
576 rtlsdr_set_agc_mode(_dev, int(automatic));
577 }
578
579 return get_gain_mode(chan);
580 }
581
get_gain_mode(size_t chan)582 bool rtl_source_c::get_gain_mode( size_t chan )
583 {
584 return _auto_gain;
585 }
586
set_gain(double gain,size_t chan)587 double rtl_source_c::set_gain( double gain, size_t chan )
588 {
589 osmosdr::gain_range_t rf_gains = rtl_source_c::get_gain_range( chan );
590
591 if (_dev) {
592 rtlsdr_set_tuner_gain( _dev, int(rf_gains.clip(gain) * 10.0) );
593 }
594
595 return get_gain( chan );
596 }
597
set_gain(double gain,const std::string & name,size_t chan)598 double rtl_source_c::set_gain( double gain, const std::string & name, size_t chan)
599 {
600 if ( "IF" == name ) {
601 return set_if_gain( gain, chan );
602 }
603
604 return set_gain( gain, chan );
605 }
606
get_gain(size_t chan)607 double rtl_source_c::get_gain( size_t chan )
608 {
609 if ( _dev )
610 return ((double)rtlsdr_get_tuner_gain( _dev )) / 10.0;
611
612 return 0;
613 }
614
get_gain(const std::string & name,size_t chan)615 double rtl_source_c::get_gain( const std::string & name, size_t chan )
616 {
617 if ( "IF" == name ) {
618 return _if_gain;
619 }
620
621 return get_gain( chan );
622 }
623
set_if_gain(double gain,size_t chan)624 double rtl_source_c::set_if_gain(double gain, size_t chan)
625 {
626 if ( _dev ) {
627 if ( rtlsdr_get_tuner_type(_dev) != RTLSDR_TUNER_E4000 ) {
628 _if_gain = 0;
629 return _if_gain;
630 }
631 }
632
633 std::vector< osmosdr::gain_range_t > if_gains;
634
635 if_gains += osmosdr::gain_range_t(-3, 6, 9);
636 if_gains += osmosdr::gain_range_t(0, 9, 3);
637 if_gains += osmosdr::gain_range_t(0, 9, 3);
638 if_gains += osmosdr::gain_range_t(0, 2, 1);
639 if_gains += osmosdr::gain_range_t(3, 15, 3);
640 if_gains += osmosdr::gain_range_t(3, 15, 3);
641
642 std::map< int, double > gains;
643
644 /* initialize with min gains */
645 for (unsigned int i = 0; i < if_gains.size(); i++) {
646 gains[ i + 1 ] = if_gains[ i ].start();
647 }
648
649 for (int i = if_gains.size() - 1; i >= 0; i--) {
650 osmosdr::gain_range_t range = if_gains[ i ];
651
652 double error = gain;
653
654 for( double g = range.start(); g <= range.stop(); g += range.step() ) {
655
656 double sum = 0;
657 for (int j = 0; j < int(gains.size()); j++) {
658 if ( i == j )
659 sum += g;
660 else
661 sum += gains[ j + 1 ];
662 }
663
664 double err = std::abs(gain - sum);
665 if (err < error) {
666 error = err;
667 gains[ i + 1 ] = g;
668 }
669 }
670 }
671 #if 0
672 std::cerr << gain << " => "; double sum = 0;
673 for (unsigned int i = 0; i < gains.size(); i++) {
674 sum += gains[ i + 1 ];
675 std::cerr << gains[ i + 1 ] << " ";
676 }
677 std::cerr << " = " << sum << std::endl;
678 #endif
679 if (_dev) {
680 for (unsigned int stage = 1; stage <= gains.size(); stage++) {
681 rtlsdr_set_tuner_if_gain( _dev, stage, int(gains[ stage ] * 10.0));
682 }
683 }
684
685 _if_gain = gain;
686 return gain;
687 }
688
get_antennas(size_t chan)689 std::vector< std::string > rtl_source_c::get_antennas( size_t chan )
690 {
691 std::vector< std::string > antennas;
692
693 antennas += get_antenna( chan );
694
695 return antennas;
696 }
697
set_antenna(const std::string & antenna,size_t chan)698 std::string rtl_source_c::set_antenna( const std::string & antenna, size_t chan )
699 {
700 return get_antenna( chan );
701 }
702
get_antenna(size_t chan)703 std::string rtl_source_c::get_antenna( size_t chan )
704 {
705 return "RX";
706 }
707