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