forked from sdr/gr-osmosdr
Compare commits
96 Commits
Author | SHA1 | Date |
---|---|---|
Dimitri Stolnikov | c7cb045fcb | |
Jon Szymaniak | b986e31e63 | |
Dimitri Stolnikov | 24d54c369f | |
Dimitri Stolnikov | 74b9211cdc | |
Jon Szymaniak | 2ae3fdbc22 | |
Brian Padalino | 79ad6f6a76 | |
Dimitri Stolnikov | 1f7e798184 | |
Hoernchen | f097a38f20 | |
Dimitri Stolnikov | 0b76b7a21b | |
Dimitri Stolnikov | 79231f6173 | |
Dimitri Stolnikov | e6701ca621 | |
Dimitri Stolnikov | 376884f6a0 | |
Jon Szymaniak | 40f93c2ae1 | |
Jon Szymaniak | 1633dbaa74 | |
Dimitri Stolnikov | 115d4ddde0 | |
Dimitri Stolnikov | 333e9bbbb2 | |
Dimitri Stolnikov | 084d193a29 | |
Marcus D. Leech | 3764dcb8af | |
Brian Padalino | 36a6b8fe1f | |
Jon Szymaniak | cd38413803 | |
Jon Szymaniak | 43af3a851e | |
Jon Szymaniak | 734ba42989 | |
Jon Szymaniak | 1e742bc186 | |
Dimitri Stolnikov | b359cb71ab | |
Dimitri Stolnikov | a832a09fd9 | |
Dimitri Stolnikov | aaedaa2c97 | |
Dimitri Stolnikov | 58648a3124 | |
Dimitri Stolnikov | becc68131c | |
Dimitri Stolnikov | 5da9aafa2c | |
Dimitri Stolnikov | f42fe3d065 | |
Dimitri Stolnikov | 3f281d9705 | |
Dimitri Stolnikov | 7b1c5dc8a0 | |
Dimitri Stolnikov | 03f9532945 | |
Dimitri Stolnikov | 0e6a369df7 | |
Steve Markgraf | 86ecf305be | |
Dimitri Stolnikov | 62127aada0 | |
Dimitri Stolnikov | 9e6f68b2e6 | |
Dimitri Stolnikov | 05e17c6417 | |
Dimitri Stolnikov | 70cd91f505 | |
Steve Markgraf | fa75a253c7 | |
Dimitri Stolnikov | b2049ba5ef | |
Jon Szymaniak | 743dc07b43 | |
Jon Szymaniak | b7fff27300 | |
Jon Szymaniak | 892433836f | |
Daniel Gröber | 54f2e08dd7 | |
Dimitri Stolnikov | 7394251bd0 | |
Dimitri Stolnikov | ee6e3d0bfa | |
Dimitri Stolnikov | 18ed13ef05 | |
Dimitri Stolnikov | 6252b4144a | |
Dimitri Stolnikov | e394dd3922 | |
Dimitri Stolnikov | 0923bcb687 | |
Dimitri Stolnikov | 96b5e4388f | |
Dimitri Stolnikov | 596790e8c1 | |
Dimitri Stolnikov | 6fa0815be0 | |
Dimitri Stolnikov | c7dcddc3cd | |
Dimitri Stolnikov | a81016d230 | |
Dimitri Stolnikov | 884ee47605 | |
Dimitri Stolnikov | 6187f182d5 | |
Dimitri Stolnikov | 2d644d2eae | |
Dimitri Stolnikov | 2dfc73351f | |
Dimitri Stolnikov | 89db739999 | |
Dimitri Stolnikov | 681e3b7d4a | |
Dimitri Stolnikov | 5d5dc04371 | |
Dimitri Stolnikov | 911f916b3b | |
Jon Szymaniak | a0b534d118 | |
Dimitri Stolnikov | 1c9a793a70 | |
Frederik M.J. Vestre | 2e9828f120 | |
Dimitri Stolnikov | c16a562ddc | |
Dimitri Stolnikov | d0a043ace3 | |
Dimitri Stolnikov | 11841ccb29 | |
Dimitri Stolnikov | 41bb9a0ef9 | |
Dimitri Stolnikov | 65e34f6863 | |
Dimitri Stolnikov | 7058fd6610 | |
Dimitri Stolnikov | 6e2a2023d5 | |
Dimitri Stolnikov | 2d02ae9b83 | |
Dimitri Stolnikov | ffd2bbf424 | |
Dimitri Stolnikov | 7b11f872c4 | |
Dimitri Stolnikov | e42752826c | |
Dimitri Stolnikov | d25666b594 | |
Dimitri Stolnikov | 6f04a9b03a | |
Dimitri Stolnikov | e1f4c24232 | |
Dimitri Stolnikov | ed627cea9c | |
Dimitri Stolnikov | f70550fe40 | |
Dimitri Stolnikov | 5a4033869f | |
Dimitri Stolnikov | faa1f44550 | |
Dimitri Stolnikov | 1822e88148 | |
Dimitri Stolnikov | 2cd4126288 | |
Dimitri Stolnikov | b59b0080f5 | |
Dimitri Stolnikov | eaec4c5acc | |
Dimitri Stolnikov | ba5c6c8da6 | |
Dimitri Stolnikov | 17c2bda9f8 | |
Dimitri Stolnikov | 9714f9cd74 | |
Dimitri Stolnikov | 0e6b6f9ec9 | |
Dimitri Stolnikov | 9cbe22f23d | |
Dimitri Stolnikov | 6a70539872 | |
Dimitri Stolnikov | fed7cfd368 |
1
AUTHORS
1
AUTHORS
|
@ -1,3 +1,4 @@
|
||||||
Dimitri Stolnikov <horiz0n@gmx.net>
|
Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
Steve Markgraf <steve@steve-m.de>
|
Steve Markgraf <steve@steve-m.de>
|
||||||
Hoernchen <la@tfc-server.de>
|
Hoernchen <la@tfc-server.de>
|
||||||
|
Nuand LLC folks
|
||||||
|
|
|
@ -36,7 +36,7 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
|
||||||
# Set the version information here
|
# Set the version information here
|
||||||
set(VERSION_INFO_MAJOR_VERSION 0)
|
set(VERSION_INFO_MAJOR_VERSION 0)
|
||||||
set(VERSION_INFO_API_COMPAT 0)
|
set(VERSION_INFO_API_COMPAT 0)
|
||||||
set(VERSION_INFO_MINOR_VERSION 1)
|
set(VERSION_INFO_MINOR_VERSION 3)
|
||||||
set(VERSION_INFO_MAINT_VERSION git)
|
set(VERSION_INFO_MAINT_VERSION git)
|
||||||
include(GrVersion) #setup version info
|
include(GrVersion) #setup version info
|
||||||
|
|
||||||
|
@ -56,10 +56,14 @@ ENDIF()
|
||||||
message(FATAL_ERROR "Option ${USE_SIMD} not supported, valid entries are ${USE_SIMD_VALUES}")
|
message(FATAL_ERROR "Option ${USE_SIMD} not supported, valid entries are ${USE_SIMD_VALUES}")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
IF(CMAKE_COMPILER_IS_GNUCXX)
|
IF(CMAKE_CXX_COMPILER MATCHES ".*clang")
|
||||||
|
SET(CMAKE_COMPILER_IS_CLANGXX 1)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
IF(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANGXX)
|
||||||
ADD_DEFINITIONS(-Wall)
|
ADD_DEFINITIONS(-Wall)
|
||||||
ADD_DEFINITIONS(-Wextra)
|
ADD_DEFINITIONS(-Wextra)
|
||||||
ADD_DEFINITIONS(-Wno-unused)
|
ADD_DEFINITIONS(-Wno-unused-parameter)
|
||||||
ADD_DEFINITIONS(-Wsign-compare)
|
ADD_DEFINITIONS(-Wsign-compare)
|
||||||
#ADD_DEFINITIONS(-Wconversion)
|
#ADD_DEFINITIONS(-Wconversion)
|
||||||
#ADD_DEFINITIONS(-pedantic)
|
#ADD_DEFINITIONS(-pedantic)
|
||||||
|
@ -86,7 +90,7 @@ ELSEIF(MSVC)
|
||||||
ADD_DEFINITIONS(/arch:AVX)
|
ADD_DEFINITIONS(/arch:AVX)
|
||||||
ADD_DEFINITIONS(-DUSE_AVX)
|
ADD_DEFINITIONS(-DUSE_AVX)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
|
ENDIF()
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Setup boost
|
# Setup boost
|
||||||
|
@ -149,10 +153,13 @@ find_package(GnuradioIQBalance)
|
||||||
find_package(UHD)
|
find_package(UHD)
|
||||||
find_package(GnuradioUHD)
|
find_package(GnuradioUHD)
|
||||||
find_package(GnuradioFCD)
|
find_package(GnuradioFCD)
|
||||||
|
find_package(GnuradioFCDPP)
|
||||||
find_package(LibOsmoSDR)
|
find_package(LibOsmoSDR)
|
||||||
find_package(LibRTLSDR)
|
find_package(LibRTLSDR)
|
||||||
find_package(LibMiriSDR)
|
find_package(LibMiriSDR)
|
||||||
find_package(LibHackRF)
|
find_package(LibHackRF)
|
||||||
|
find_package(LibAIRSPY)
|
||||||
|
find_package(LibbladeRF)
|
||||||
find_package(Doxygen)
|
find_package(Doxygen)
|
||||||
|
|
||||||
if(NOT GRUEL_FOUND)
|
if(NOT GRUEL_FOUND)
|
||||||
|
@ -199,7 +206,7 @@ add_custom_target(uninstall
|
||||||
########################################################################
|
########################################################################
|
||||||
# Enable python component
|
# Enable python component
|
||||||
########################################################################
|
########################################################################
|
||||||
find_package(PythonLibs)
|
find_package(PythonLibs 2)
|
||||||
find_package(SWIG)
|
find_package(SWIG)
|
||||||
|
|
||||||
if(SWIG_FOUND)
|
if(SWIG_FOUND)
|
||||||
|
|
17
README
17
README
|
@ -1,13 +1,18 @@
|
||||||
While primarily being developed for the OsmoSDR hardware, this block as well supports:
|
While primarily being developed for the OsmoSDR hardware, this block as well supports:
|
||||||
|
|
||||||
* FunCube Dongle through libgnuradio-fcd
|
* FUNcube Dongle through libgnuradio-fcd
|
||||||
* OSMOCOM OsmoSDR Devices through libosmosdr
|
* FUNcube Dongle Pro+ through gr-fcdproplus
|
||||||
* Great Scott Gadgets HackRF through libhackrf
|
* sysmocom OsmoSDR Devices through libosmosdr
|
||||||
* Ettus USRP Devices through Ettus UHD library
|
|
||||||
* RTL2832U based DVB-T dongles through librtlsdr
|
* RTL2832U based DVB-T dongles through librtlsdr
|
||||||
* RTL-TCP spectrum server (see librtlsdr project)
|
* RTL-TCP spectrum server (see librtlsdr project)
|
||||||
* MSi2500 based DVB-T dongles through libmirisdr
|
* MSi2500 based DVB-T dongles through libmirisdr
|
||||||
* gnuradio .cfile input through libgnuradio-core
|
* gnuradio .cfile input through libgnuradio-blocks
|
||||||
|
* RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option)
|
||||||
|
* AirSpy Wideband Receiver through libairspy
|
||||||
|
* Great Scott Gadgets HackRF through libhackrf
|
||||||
|
* Nuand LLC bladeRF through libbladeRF library
|
||||||
|
* Ettus USRP Devices through Ettus UHD library
|
||||||
|
* Fairwaves UmTRX through Fairwaves' fork of UHD
|
||||||
|
|
||||||
By using the OsmoSDR block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
|
By using the OsmoSDR block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
|
||||||
|
|
||||||
|
@ -32,4 +37,4 @@ make
|
||||||
sudo make install
|
sudo make install
|
||||||
sudo ldconfig
|
sudo ldconfig
|
||||||
|
|
||||||
NOTE: The OSMOCOM source block will appear under 'Sources' category in GRC menu.
|
NOTE: The osmocom source block will appear under 'Sources' category in GRC menu.
|
||||||
|
|
349
apps/osmocom_fft
349
apps/osmocom_fft
|
@ -66,10 +66,14 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
help="Set sample rate (bandwidth), minimum by default")
|
help="Set sample rate (bandwidth), minimum by default")
|
||||||
parser.add_option("-f", "--center-freq", type="eng_float", default=None,
|
parser.add_option("-f", "--center-freq", type="eng_float", default=None,
|
||||||
help="Set frequency to FREQ", metavar="FREQ")
|
help="Set frequency to FREQ", metavar="FREQ")
|
||||||
parser.add_option("-c", "--freq-corr", type="eng_float", default=0,
|
parser.add_option("-c", "--freq-corr", type="eng_float", default=None,
|
||||||
help="Set frequency correction (ppm)")
|
help="Set frequency correction (ppm)")
|
||||||
parser.add_option("-g", "--gain", type="eng_float", default=None,
|
parser.add_option("-g", "--gain", type="eng_float", default=None,
|
||||||
help="Set gain in dB (default is midpoint)")
|
help="Set gain in dB (default is midpoint)")
|
||||||
|
parser.add_option("", "--dc-offset-mode", type="int", default=None,
|
||||||
|
help="Set the RX frontend DC offset correction mode")
|
||||||
|
parser.add_option("", "--iq-balance-mode", type="int", default=None,
|
||||||
|
help="Set the RX frontend IQ imbalance correction mode")
|
||||||
parser.add_option("-W", "--waterfall", action="store_true", default=False,
|
parser.add_option("-W", "--waterfall", action="store_true", default=False,
|
||||||
help="Enable waterfall display")
|
help="Enable waterfall display")
|
||||||
parser.add_option("-S", "--oscilloscope", action="store_true", default=False,
|
parser.add_option("-S", "--oscilloscope", action="store_true", default=False,
|
||||||
|
@ -97,6 +101,12 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
|
|
||||||
self.src = osmosdr.source_c(options.args)
|
self.src = osmosdr.source_c(options.args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.src.get_sample_rates().start()
|
||||||
|
except RuntimeError:
|
||||||
|
print "Source has no sample rates (wrong device arguments?)."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# Set the antenna
|
# Set the antenna
|
||||||
if(options.antenna):
|
if(options.antenna):
|
||||||
self.src.set_antenna(options.antenna)
|
self.src.set_antenna(options.antenna)
|
||||||
|
@ -105,21 +115,32 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
options.samp_rate = self.src.get_sample_rates().start()
|
options.samp_rate = self.src.get_sample_rates().start()
|
||||||
|
|
||||||
if options.gain is None:
|
if options.gain is None:
|
||||||
|
gain = self.src.get_gain()
|
||||||
|
if gain is None:
|
||||||
# if no gain was specified, use the mid-point in dB
|
# if no gain was specified, use the mid-point in dB
|
||||||
r = self.src.get_gain_range()
|
r = self.src.get_gain_range()
|
||||||
|
try: # empty gain range returned in file= mode
|
||||||
options.gain = float(r.start()+r.stop())/2
|
options.gain = float(r.start()+r.stop())/2
|
||||||
|
except RuntimeError:
|
||||||
|
options.gain = 0
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
options.gain = gain
|
||||||
|
|
||||||
if options.center_freq is None:
|
if options.center_freq is None:
|
||||||
|
freq = self.src.get_center_freq()
|
||||||
|
if freq != 0:
|
||||||
|
options.center_freq = freq
|
||||||
|
else:
|
||||||
# if no freq was specified, use the mid-point in Hz
|
# if no freq was specified, use the mid-point in Hz
|
||||||
r = self.src.get_freq_range()
|
r = self.src.get_freq_range()
|
||||||
options.center_freq = float(r.start()+r.stop())/2
|
options.center_freq = float(r.start()+r.stop())/2
|
||||||
|
|
||||||
input_rate = self.src.set_sample_rate(options.samp_rate)
|
input_rate = self.src.set_sample_rate(options.samp_rate)
|
||||||
|
self.src.set_bandwidth(input_rate)
|
||||||
|
|
||||||
self.src.set_gain(options.gain)
|
self.src.set_gain(options.gain)
|
||||||
|
|
||||||
#print self.src.get_sample_rates().to_pp_string()
|
|
||||||
|
|
||||||
self.publish(SAMP_RANGE_KEY, self.src.get_sample_rates)
|
self.publish(SAMP_RANGE_KEY, self.src.get_sample_rates)
|
||||||
self.publish(FREQ_RANGE_KEY, self.src.get_freq_range)
|
self.publish(FREQ_RANGE_KEY, self.src.get_freq_range)
|
||||||
|
|
||||||
|
@ -139,6 +160,15 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
self[CENTER_FREQ_KEY] = options.center_freq
|
self[CENTER_FREQ_KEY] = options.center_freq
|
||||||
self[FREQ_CORR_KEY] = options.freq_corr
|
self[FREQ_CORR_KEY] = options.freq_corr
|
||||||
|
|
||||||
|
self.dc_offset_mode = options.dc_offset_mode
|
||||||
|
self.iq_balance_mode = options.iq_balance_mode
|
||||||
|
|
||||||
|
# initialize reasonable defaults for DC / IQ correction
|
||||||
|
self['dc_offset_real'] = 0
|
||||||
|
self['dc_offset_imag'] = 0
|
||||||
|
self['iq_balance_mag'] = 0
|
||||||
|
self['iq_balance_pha'] = 0
|
||||||
|
|
||||||
#subscribe set methods
|
#subscribe set methods
|
||||||
self.subscribe(SAMP_RATE_KEY, self.set_sample_rate)
|
self.subscribe(SAMP_RATE_KEY, self.set_sample_rate)
|
||||||
|
|
||||||
|
@ -149,18 +179,28 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
self.subscribe(CENTER_FREQ_KEY, self.set_freq)
|
self.subscribe(CENTER_FREQ_KEY, self.set_freq)
|
||||||
self.subscribe(FREQ_CORR_KEY, self.set_freq_corr)
|
self.subscribe(FREQ_CORR_KEY, self.set_freq_corr)
|
||||||
|
|
||||||
|
self.subscribe('dc_offset_real', self.set_dc_offset)
|
||||||
|
self.subscribe('dc_offset_imag', self.set_dc_offset)
|
||||||
|
self.subscribe('iq_balance_mag', self.set_iq_balance)
|
||||||
|
self.subscribe('iq_balance_pha', self.set_iq_balance)
|
||||||
|
|
||||||
#force update on pubsub keys
|
#force update on pubsub keys
|
||||||
for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY):
|
#for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY):
|
||||||
print key, "=", self[key]
|
#print key, "=", self[key]
|
||||||
#self[key] = self[key]
|
#self[key] = self[key]
|
||||||
|
|
||||||
if options.waterfall:
|
if options.waterfall:
|
||||||
self.scope = \
|
self.scope = waterfallsink2.waterfall_sink_c (panel,
|
||||||
waterfallsink2.waterfallsrc_c (panel, fft_size=options.fft_size,
|
fft_size=options.fft_size,
|
||||||
sample_rate=input_rate)
|
sample_rate=input_rate,
|
||||||
|
ref_scale=options.ref_scale,
|
||||||
|
ref_level=20.0,
|
||||||
|
y_divs = 12)
|
||||||
|
|
||||||
|
self.scope.set_callback(self.wxsink_callback)
|
||||||
self.frame.SetMinSize((800, 420))
|
self.frame.SetMinSize((800, 420))
|
||||||
elif options.oscilloscope:
|
elif options.oscilloscope:
|
||||||
self.scope = scopesink2.scopesrc_c(panel, sample_rate=input_rate)
|
self.scope = scopesink2.scope_sink_c(panel, sample_rate=input_rate)
|
||||||
self.frame.SetMinSize((800, 600))
|
self.frame.SetMinSize((800, 600))
|
||||||
else:
|
else:
|
||||||
self.scope = fftsink2.fft_sink_c (panel,
|
self.scope = fftsink2.fft_sink_c (panel,
|
||||||
|
@ -172,21 +212,27 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
average=options.averaging,
|
average=options.averaging,
|
||||||
avg_alpha=options.avg_alpha,
|
avg_alpha=options.avg_alpha,
|
||||||
fft_rate=options.fft_rate)
|
fft_rate=options.fft_rate)
|
||||||
def fftsink_callback(x, y):
|
|
||||||
self.set_freq(x)
|
|
||||||
|
|
||||||
self.scope.set_callback(fftsink_callback)
|
self.scope.set_callback(self.wxsink_callback)
|
||||||
self.frame.SetMinSize((800, 420))
|
self.frame.SetMinSize((800, 420))
|
||||||
|
|
||||||
self.connect(self.src, self.scope)
|
self.connect(self.src, self.scope)
|
||||||
|
|
||||||
self._build_gui(vbox)
|
self._build_gui(vbox)
|
||||||
self._setup_events()
|
|
||||||
|
if self.dc_offset_mode != None:
|
||||||
|
self.set_dc_offset_mode(self.dc_offset_mode)
|
||||||
|
|
||||||
|
if self.iq_balance_mode != None:
|
||||||
|
self.set_iq_balance_mode(self.iq_balance_mode)
|
||||||
|
|
||||||
# set initial values
|
# set initial values
|
||||||
if not(self.set_freq(options.center_freq)):
|
if not(self.set_freq(options.center_freq)):
|
||||||
self._set_status_msg("Failed to set initial frequency")
|
self._set_status_msg("Failed to set initial frequency")
|
||||||
|
|
||||||
|
def wxsink_callback(self, x, y):
|
||||||
|
self.set_freq_from_callback(x)
|
||||||
|
|
||||||
def _set_status_msg(self, msg):
|
def _set_status_msg(self, msg):
|
||||||
self.frame.GetStatusBar().SetStatusText(msg, 0)
|
self.frame.GetStatusBar().SetStatusText(msg, 0)
|
||||||
|
|
||||||
|
@ -231,6 +277,8 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
)
|
)
|
||||||
freq_hbox.AddSpacer(5)
|
freq_hbox.AddSpacer(5)
|
||||||
|
|
||||||
|
try: # range.start() == range.stop() in file= mode
|
||||||
|
|
||||||
forms.slider(
|
forms.slider(
|
||||||
parent=self.panel, sizer=freq_hbox,
|
parent=self.panel, sizer=freq_hbox,
|
||||||
proportion=3,
|
proportion=3,
|
||||||
|
@ -238,16 +286,21 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
key=CENTER_FREQ_KEY,
|
key=CENTER_FREQ_KEY,
|
||||||
minimum=self[FREQ_RANGE_KEY].start(),
|
minimum=self[FREQ_RANGE_KEY].start(),
|
||||||
maximum=self[FREQ_RANGE_KEY].stop(),
|
maximum=self[FREQ_RANGE_KEY].stop(),
|
||||||
num_steps=101,
|
num_steps=1000,
|
||||||
)
|
)
|
||||||
freq_hbox.AddSpacer(3)
|
freq_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
except AssertionError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if self[FREQ_CORR_KEY] != None: # show frequency correction scrollbar
|
||||||
|
|
||||||
corr_hbox.AddSpacer(3)
|
corr_hbox.AddSpacer(3)
|
||||||
forms.text_box(
|
forms.text_box(
|
||||||
parent=self.panel, sizer=corr_hbox,
|
parent=self.panel, sizer=corr_hbox,
|
||||||
label='Freq. Correction (ppm)',
|
label='Freq. Correction (ppm)',
|
||||||
proportion=1,
|
proportion=1,
|
||||||
converter=forms.int_converter(),
|
converter=forms.float_converter(),
|
||||||
ps=self,
|
ps=self,
|
||||||
key=FREQ_CORR_KEY,
|
key=FREQ_CORR_KEY,
|
||||||
)
|
)
|
||||||
|
@ -260,8 +313,8 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
key=FREQ_CORR_KEY,
|
key=FREQ_CORR_KEY,
|
||||||
minimum=-100,
|
minimum=-100,
|
||||||
maximum=+100,
|
maximum=+100,
|
||||||
num_steps=201,
|
num_steps=2010,
|
||||||
step_size=1,
|
step_size=0.1,
|
||||||
)
|
)
|
||||||
corr_hbox.AddSpacer(3)
|
corr_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
@ -306,7 +359,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
key=GAIN_KEY(gain_name),
|
key=GAIN_KEY(gain_name),
|
||||||
minimum=range.start(),
|
minimum=range.start(),
|
||||||
maximum=range.stop(),
|
maximum=range.stop(),
|
||||||
step_size=range.step(),
|
step_size=range.step() or (range.stop() - range.start())/10,
|
||||||
)
|
)
|
||||||
gain_hbox.AddSpacer(3)
|
gain_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
@ -317,8 +370,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
|
|
||||||
bw_range = self[BWIDTH_RANGE_KEY]
|
bw_range = self[BWIDTH_RANGE_KEY]
|
||||||
#print bw_range.to_pp_string()
|
#print bw_range.to_pp_string()
|
||||||
#if bw_range.start() < bw_range.stop():
|
if bw_range.start() < bw_range.stop():
|
||||||
if 0:
|
|
||||||
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
|
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
label="Bandwidth",
|
label="Bandwidth",
|
||||||
orient=wx.VERTICAL,
|
orient=wx.VERTICAL,
|
||||||
|
@ -349,7 +401,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
key=BWIDTH_KEY,
|
key=BWIDTH_KEY,
|
||||||
minimum=bw_range.start(),
|
minimum=bw_range.start(),
|
||||||
maximum=bw_range.stop(),
|
maximum=bw_range.stop(),
|
||||||
step_size=bw_range.step(),
|
step_size=bw_range.step() or (bw_range.stop() - bw_range.start())/100,
|
||||||
)
|
)
|
||||||
bwidth_hbox.AddSpacer(3)
|
bwidth_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
@ -397,11 +449,227 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
#)
|
#)
|
||||||
#sr_hbox.AddSpacer(3)
|
#sr_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# DC Offset controls
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
if self.dc_offset_mode != None:
|
||||||
|
|
||||||
|
dc_offset_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
|
label="DC Offset Correction",
|
||||||
|
orient=wx.VERTICAL,
|
||||||
|
bold=True)
|
||||||
|
dc_offset_vbox.AddSpacer(3)
|
||||||
|
# First row of sample rate controls
|
||||||
|
dc_offset_hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
dc_offset_vbox.Add(dc_offset_hbox, 0, wx.EXPAND)
|
||||||
|
dc_offset_vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
# Add frequency controls to top window sizer
|
||||||
|
vbox.Add(dc_offset_vbox, 0, wx.EXPAND)
|
||||||
|
vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.dc_offset_mode_chooser = forms.radio_buttons(
|
||||||
|
parent=self.panel,
|
||||||
|
value=self.dc_offset_mode,
|
||||||
|
callback=self.set_dc_offset_mode,
|
||||||
|
label='',
|
||||||
|
choices=[0, 1, 2],
|
||||||
|
labels=["Off", "Manual", "Auto"],
|
||||||
|
style=wx.RA_HORIZONTAL,
|
||||||
|
)
|
||||||
|
dc_offset_hbox.Add(self.dc_offset_mode_chooser)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
self.dc_offset_real_text = forms.text_box(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
label='Real',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self,
|
||||||
|
key='dc_offset_real',
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.dc_offset_real_slider = forms.slider(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self,
|
||||||
|
key='dc_offset_real',
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
self.dc_offset_imag_text = forms.text_box(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
label='Imag',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self,
|
||||||
|
key='dc_offset_imag',
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.dc_offset_imag_slider = forms.slider(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self,
|
||||||
|
key='dc_offset_imag',
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# IQ Imbalance controls
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
if self.iq_balance_mode != None:
|
||||||
|
|
||||||
|
iq_balance_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
|
label="IQ Imbalance Correction",
|
||||||
|
orient=wx.VERTICAL,
|
||||||
|
bold=True)
|
||||||
|
iq_balance_vbox.AddSpacer(3)
|
||||||
|
# First row of sample rate controls
|
||||||
|
iq_balance_hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
iq_balance_vbox.Add(iq_balance_hbox, 0, wx.EXPAND)
|
||||||
|
iq_balance_vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
# Add frequency controls to top window sizer
|
||||||
|
vbox.Add(iq_balance_vbox, 0, wx.EXPAND)
|
||||||
|
vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.iq_balance_mode_chooser = forms.radio_buttons(
|
||||||
|
parent=self.panel,
|
||||||
|
value=self.iq_balance_mode,
|
||||||
|
callback=self.set_iq_balance_mode,
|
||||||
|
label='',
|
||||||
|
choices=[0, 1, 2],
|
||||||
|
labels=["Off", "Manual", "Auto"],
|
||||||
|
style=wx.RA_HORIZONTAL,
|
||||||
|
)
|
||||||
|
iq_balance_hbox.Add(self.iq_balance_mode_chooser)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
self.iq_balance_mag_text = forms.text_box(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
label='Mag',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self,
|
||||||
|
key='iq_balance_mag',
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.iq_balance_mag_slider = forms.slider(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self,
|
||||||
|
key='iq_balance_mag',
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
self.iq_balance_pha_text = forms.text_box(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
label='Phase',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self,
|
||||||
|
key='iq_balance_pha',
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
self.iq_balance_pha_slider = forms.slider(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self,
|
||||||
|
key='iq_balance_pha',
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
def set_dc_offset_mode(self, dc_offset_mode):
|
||||||
|
if dc_offset_mode == 1:
|
||||||
|
self.dc_offset_real_text.Enable()
|
||||||
|
self.dc_offset_real_slider.Enable()
|
||||||
|
self.dc_offset_imag_text.Enable()
|
||||||
|
self.dc_offset_imag_slider.Enable()
|
||||||
|
|
||||||
|
self.set_dc_offset(0)
|
||||||
|
else:
|
||||||
|
self.dc_offset_real_text.Disable()
|
||||||
|
self.dc_offset_real_slider.Disable()
|
||||||
|
self.dc_offset_imag_text.Disable()
|
||||||
|
self.dc_offset_imag_slider.Disable()
|
||||||
|
|
||||||
|
self.dc_offset_mode = dc_offset_mode
|
||||||
|
self.src.set_dc_offset_mode(dc_offset_mode)
|
||||||
|
self.dc_offset_mode_chooser.set_value(self.dc_offset_mode)
|
||||||
|
|
||||||
|
def set_dc_offset(self, value):
|
||||||
|
correction = complex( self['dc_offset_real'], self['dc_offset_imag'] )
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.src.set_dc_offset( correction )
|
||||||
|
|
||||||
|
if self._verbose:
|
||||||
|
print "Set DC offset to", correction
|
||||||
|
except RuntimeError as ex:
|
||||||
|
print ex
|
||||||
|
|
||||||
|
def set_iq_balance_mode(self, iq_balance_mode):
|
||||||
|
if iq_balance_mode == 1:
|
||||||
|
self.iq_balance_mag_text.Enable()
|
||||||
|
self.iq_balance_mag_slider.Enable()
|
||||||
|
self.iq_balance_pha_text.Enable()
|
||||||
|
self.iq_balance_pha_slider.Enable()
|
||||||
|
|
||||||
|
self.set_iq_balance(0)
|
||||||
|
else:
|
||||||
|
self.iq_balance_mag_text.Disable()
|
||||||
|
self.iq_balance_mag_slider.Disable()
|
||||||
|
self.iq_balance_pha_text.Disable()
|
||||||
|
self.iq_balance_pha_slider.Disable()
|
||||||
|
|
||||||
|
self.iq_balance_mode = iq_balance_mode
|
||||||
|
self.src.set_iq_balance_mode(iq_balance_mode)
|
||||||
|
self.iq_balance_mode_chooser.set_value(self.iq_balance_mode)
|
||||||
|
|
||||||
|
def set_iq_balance(self, value):
|
||||||
|
correction = complex( self['iq_balance_mag'], self['iq_balance_pha'] )
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.src.set_iq_balance( correction )
|
||||||
|
|
||||||
|
if self._verbose:
|
||||||
|
print "Set IQ balance to", correction
|
||||||
|
except RuntimeError as ex:
|
||||||
|
print ex
|
||||||
|
|
||||||
def set_sample_rate(self, samp_rate):
|
def set_sample_rate(self, samp_rate):
|
||||||
samp_rate = self.src.set_sample_rate(samp_rate)
|
samp_rate = self.src.set_sample_rate(samp_rate)
|
||||||
self.scope.set_sample_rate(samp_rate)
|
self.scope.set_sample_rate(samp_rate)
|
||||||
if self._verbose:
|
if self._verbose:
|
||||||
print "Set sample rate to:", samp_rate
|
print "Set sample rate to:", samp_rate
|
||||||
|
|
||||||
|
try:
|
||||||
|
self[BWIDTH_KEY] = self.set_bandwidth(samp_rate)
|
||||||
|
except RuntimeError:
|
||||||
|
pass
|
||||||
|
|
||||||
return samp_rate
|
return samp_rate
|
||||||
|
|
||||||
def get_gain_names(self):
|
def get_gain_names(self):
|
||||||
|
@ -421,10 +689,19 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
print "Set " + name + " gain to:", gain
|
print "Set " + name + " gain to:", gain
|
||||||
|
|
||||||
def set_bandwidth(self, bw):
|
def set_bandwidth(self, bw):
|
||||||
bw = self.src.set_bandwidth(bw)
|
clipped_bw = self[BWIDTH_RANGE_KEY].clip(bw)
|
||||||
|
if self.src.get_bandwidth() != clipped_bw:
|
||||||
|
bw = self.src.set_bandwidth(clipped_bw)
|
||||||
|
|
||||||
if self._verbose:
|
if self._verbose:
|
||||||
print "Set bandwidth to:", bw
|
print "Set bandwidth to:", bw
|
||||||
|
|
||||||
|
return bw
|
||||||
|
|
||||||
|
def set_freq_from_callback(self, freq):
|
||||||
|
freq = self.src.set_center_freq(freq)
|
||||||
|
self[CENTER_FREQ_KEY] = freq;
|
||||||
|
|
||||||
def set_freq(self, freq):
|
def set_freq(self, freq):
|
||||||
if freq is None:
|
if freq is None:
|
||||||
f = self[FREQ_RANGE_KEY]
|
f = self[FREQ_RANGE_KEY]
|
||||||
|
@ -458,34 +735,8 @@ class app_top_block(stdgui2.std_top_block, pubsub):
|
||||||
if self._verbose:
|
if self._verbose:
|
||||||
print "Set frequency correction to:", ppm
|
print "Set frequency correction to:", ppm
|
||||||
|
|
||||||
def _setup_events(self):
|
|
||||||
if not self.options.waterfall and not self.options.oscilloscope:
|
|
||||||
self.scope.win.Bind(wx.EVT_LEFT_DCLICK, self.evt_left_dclick)
|
|
||||||
|
|
||||||
def evt_left_dclick(self, event):
|
|
||||||
(ux, uy) = self.scope.win.GetXY(event)
|
|
||||||
if event.CmdDown():
|
|
||||||
# Re-center on maximum power
|
|
||||||
points = self.scope.win._points
|
|
||||||
if self.scope.win.peak_hold:
|
|
||||||
if self.scope.win.peak_vals is not None:
|
|
||||||
ind = numpy.argmax(self.scope.win.peak_vals)
|
|
||||||
else:
|
|
||||||
ind = int(points.shape()[0]/2)
|
|
||||||
else:
|
|
||||||
ind = numpy.argmax(points[:,1])
|
|
||||||
|
|
||||||
(freq, pwr) = points[ind]
|
|
||||||
target_freq = freq/self.scope.win._scale_factor
|
|
||||||
print ind, freq, pwr
|
|
||||||
self.set_freq(target_freq)
|
|
||||||
else:
|
|
||||||
# Re-center on clicked frequency
|
|
||||||
target_freq = ux/self.scope.win._scale_factor
|
|
||||||
self.set_freq(target_freq)
|
|
||||||
|
|
||||||
def main ():
|
def main ():
|
||||||
app = stdgui2.stdapp(app_top_block, "OSMOCOM Spectrum Browser", nstatus=1)
|
app = stdgui2.stdapp(app_top_block, "osmocom Spectrum Browser", nstatus=1)
|
||||||
app.MainLoop()
|
app.MainLoop()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -179,7 +179,7 @@ class app_gui(pubsub):
|
||||||
parent=self.panel, sizer=corr_hbox,
|
parent=self.panel, sizer=corr_hbox,
|
||||||
label='Freq. Correction (ppm)',
|
label='Freq. Correction (ppm)',
|
||||||
proportion=1,
|
proportion=1,
|
||||||
converter=forms.int_converter(),
|
converter=forms.float_converter(),
|
||||||
ps=self.tb,
|
ps=self.tb,
|
||||||
key=osmocom_siggen.FREQ_CORR_KEY,
|
key=osmocom_siggen.FREQ_CORR_KEY,
|
||||||
)
|
)
|
||||||
|
@ -192,8 +192,8 @@ class app_gui(pubsub):
|
||||||
key=osmocom_siggen.FREQ_CORR_KEY,
|
key=osmocom_siggen.FREQ_CORR_KEY,
|
||||||
minimum=-100,
|
minimum=-100,
|
||||||
maximum=+100,
|
maximum=+100,
|
||||||
num_steps=201,
|
num_steps=2010,
|
||||||
step_size=1,
|
step_size=0.1,
|
||||||
)
|
)
|
||||||
corr_hbox.AddSpacer(3)
|
corr_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
@ -275,8 +275,7 @@ class app_gui(pubsub):
|
||||||
|
|
||||||
bw_range = self.tb[osmocom_siggen.BWIDTH_RANGE_KEY]
|
bw_range = self.tb[osmocom_siggen.BWIDTH_RANGE_KEY]
|
||||||
#print bw_range.to_pp_string()
|
#print bw_range.to_pp_string()
|
||||||
#if bw_range.start() < bw_range.stop():
|
if bw_range.start() < bw_range.stop():
|
||||||
if 0:
|
|
||||||
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
|
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
label="Bandwidth",
|
label="Bandwidth",
|
||||||
orient=wx.VERTICAL,
|
orient=wx.VERTICAL,
|
||||||
|
@ -314,6 +313,130 @@ class app_gui(pubsub):
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# DC Offset controls
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
dc_offset_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
|
label="DC Offset Correction",
|
||||||
|
orient=wx.VERTICAL,
|
||||||
|
bold=True)
|
||||||
|
dc_offset_vbox.AddSpacer(3)
|
||||||
|
# First row of sample rate controls
|
||||||
|
dc_offset_hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
dc_offset_vbox.Add(dc_offset_hbox, 0, wx.EXPAND)
|
||||||
|
dc_offset_vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
# Add frequency controls to top window sizer
|
||||||
|
self.vbox.Add(dc_offset_vbox, 0, wx.EXPAND)
|
||||||
|
self.vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
forms.text_box(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
label='Real',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.DC_OFFSET_REAL
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
forms.slider(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.DC_OFFSET_REAL
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
forms.text_box(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
label='Imag',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.DC_OFFSET_IMAG
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
forms.slider(
|
||||||
|
parent=self.panel, sizer=dc_offset_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.DC_OFFSET_IMAG
|
||||||
|
)
|
||||||
|
dc_offset_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
##################################################
|
||||||
|
# IQ Imbalance controls
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
iq_balance_vbox = forms.static_box_sizer(parent=self.panel,
|
||||||
|
label="IQ Imbalance Correction",
|
||||||
|
orient=wx.VERTICAL,
|
||||||
|
bold=True)
|
||||||
|
iq_balance_vbox.AddSpacer(3)
|
||||||
|
# First row of sample rate controls
|
||||||
|
iq_balance_hbox = wx.BoxSizer(wx.HORIZONTAL)
|
||||||
|
iq_balance_vbox.Add(iq_balance_hbox, 0, wx.EXPAND)
|
||||||
|
iq_balance_vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
# Add frequency controls to top window sizer
|
||||||
|
self.vbox.Add(iq_balance_vbox, 0, wx.EXPAND)
|
||||||
|
self.vbox.AddSpacer(3)
|
||||||
|
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
forms.text_box(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
label='Mag',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.IQ_BALANCE_MAG
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
forms.slider(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.IQ_BALANCE_MAG
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
forms.text_box(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
label='Phase',
|
||||||
|
proportion=1,
|
||||||
|
converter=forms.float_converter(),
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.IQ_BALANCE_PHA
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
|
forms.slider(
|
||||||
|
parent=self.panel, sizer=iq_balance_hbox,
|
||||||
|
proportion=3,
|
||||||
|
minimum=-1,
|
||||||
|
maximum=+1,
|
||||||
|
step_size=0.001,
|
||||||
|
ps=self.tb,
|
||||||
|
key=osmocom_siggen.IQ_BALANCE_PHA
|
||||||
|
)
|
||||||
|
iq_balance_hbox.AddSpacer(3)
|
||||||
|
|
||||||
##################################################
|
##################################################
|
||||||
# Sample Rate controls
|
# Sample Rate controls
|
||||||
##################################################
|
##################################################
|
||||||
|
@ -347,7 +470,7 @@ def main():
|
||||||
gui=app_gui, # User interface class
|
gui=app_gui, # User interface class
|
||||||
options=options, # Command line options
|
options=options, # Command line options
|
||||||
args=args, # Command line args
|
args=args, # Command line args
|
||||||
title="OSMOCOM Signal Generator", # Top window title
|
title="osmocom Signal Generator", # Top window title
|
||||||
nstatus=1, # Number of status lines
|
nstatus=1, # Number of status lines
|
||||||
start=True, # Whether to start flowgraph
|
start=True, # Whether to start flowgraph
|
||||||
realtime=True) # Whether to set realtime priority
|
realtime=True) # Whether to set realtime priority
|
||||||
|
|
|
@ -33,6 +33,10 @@ WAVEFORM2_FREQ_KEY = 'waveform2_freq'
|
||||||
FREQ_RANGE_KEY = 'freq_range'
|
FREQ_RANGE_KEY = 'freq_range'
|
||||||
GAIN_RANGE_KEY = lambda x: 'gain_range:'+x
|
GAIN_RANGE_KEY = lambda x: 'gain_range:'+x
|
||||||
BWIDTH_RANGE_KEY = 'bwidth_range'
|
BWIDTH_RANGE_KEY = 'bwidth_range'
|
||||||
|
DC_OFFSET_REAL = 'dc_offset_real'
|
||||||
|
DC_OFFSET_IMAG = 'dc_offset_imag'
|
||||||
|
IQ_BALANCE_MAG = 'iq_balance_mag'
|
||||||
|
IQ_BALANCE_PHA = 'iq_balance_pha'
|
||||||
TYPE_KEY = 'type'
|
TYPE_KEY = 'type'
|
||||||
|
|
||||||
def setter(ps, key, val): ps[key] = val
|
def setter(ps, key, val): ps[key] = val
|
||||||
|
@ -134,6 +138,12 @@ class top_block(gr.top_block, pubsub):
|
||||||
self[WAVEFORM_OFFSET_KEY] = options.offset
|
self[WAVEFORM_OFFSET_KEY] = options.offset
|
||||||
self[WAVEFORM2_FREQ_KEY] = options.waveform2_freq
|
self[WAVEFORM2_FREQ_KEY] = options.waveform2_freq
|
||||||
|
|
||||||
|
# initialize reasonable defaults for DC / IQ correction
|
||||||
|
self[DC_OFFSET_REAL] = 0
|
||||||
|
self[DC_OFFSET_IMAG] = 0
|
||||||
|
self[IQ_BALANCE_MAG] = 0
|
||||||
|
self[IQ_BALANCE_PHA] = 0
|
||||||
|
|
||||||
#subscribe set methods
|
#subscribe set methods
|
||||||
self.subscribe(SAMP_RATE_KEY, self.set_samp_rate)
|
self.subscribe(SAMP_RATE_KEY, self.set_samp_rate)
|
||||||
|
|
||||||
|
@ -148,6 +158,11 @@ class top_block(gr.top_block, pubsub):
|
||||||
self.subscribe(WAVEFORM2_FREQ_KEY, self.set_waveform2_freq)
|
self.subscribe(WAVEFORM2_FREQ_KEY, self.set_waveform2_freq)
|
||||||
self.subscribe(TYPE_KEY, self.set_waveform)
|
self.subscribe(TYPE_KEY, self.set_waveform)
|
||||||
|
|
||||||
|
self.subscribe(DC_OFFSET_REAL, self.set_dc_offset)
|
||||||
|
self.subscribe(DC_OFFSET_IMAG, self.set_dc_offset)
|
||||||
|
self.subscribe(IQ_BALANCE_MAG, self.set_iq_balance)
|
||||||
|
self.subscribe(IQ_BALANCE_PHA, self.set_iq_balance)
|
||||||
|
|
||||||
#force update on pubsub keys
|
#force update on pubsub keys
|
||||||
for key in (SAMP_RATE_KEY, GAIN_KEY, BWIDTH_KEY,
|
for key in (SAMP_RATE_KEY, GAIN_KEY, BWIDTH_KEY,
|
||||||
TX_FREQ_KEY, FREQ_CORR_KEY, AMPLITUDE_KEY,
|
TX_FREQ_KEY, FREQ_CORR_KEY, AMPLITUDE_KEY,
|
||||||
|
@ -159,6 +174,12 @@ class top_block(gr.top_block, pubsub):
|
||||||
def _setup_osmosdr(self, options):
|
def _setup_osmosdr(self, options):
|
||||||
self._sink = osmosdr.sink_c(options.args)
|
self._sink = osmosdr.sink_c(options.args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._sink.get_sample_rates().start()
|
||||||
|
except RuntimeError:
|
||||||
|
print "Sink has no sample rates (wrong device arguments?)."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if options.samp_rate is None:
|
if options.samp_rate is None:
|
||||||
options.samp_rate = self._sink.get_sample_rates().start()
|
options.samp_rate = self._sink.get_sample_rates().start()
|
||||||
|
|
||||||
|
@ -226,10 +247,35 @@ class top_block(gr.top_block, pubsub):
|
||||||
print "Set " + name + " gain to:", gain
|
print "Set " + name + " gain to:", gain
|
||||||
|
|
||||||
def set_bandwidth(self, bw):
|
def set_bandwidth(self, bw):
|
||||||
bw = self._sink.set_bandwidth(bw)
|
clipped_bw = self[BWIDTH_RANGE_KEY].clip(bw)
|
||||||
|
if self._sink.get_bandwidth() != clipped_bw:
|
||||||
|
bw = self._sink.set_bandwidth(clipped_bw)
|
||||||
|
|
||||||
if self._verbose:
|
if self._verbose:
|
||||||
print "Set bandwidth to:", bw
|
print "Set bandwidth to:", bw
|
||||||
|
|
||||||
|
def set_dc_offset(self, value):
|
||||||
|
correction = complex( self[DC_OFFSET_REAL], self[DC_OFFSET_IMAG] )
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._sink.set_dc_offset( correction )
|
||||||
|
|
||||||
|
if self._verbose:
|
||||||
|
print "Set DC offset to", correction
|
||||||
|
except RuntimeError as ex:
|
||||||
|
print ex
|
||||||
|
|
||||||
|
def set_iq_balance(self, value):
|
||||||
|
correction = complex( self[IQ_BALANCE_MAG], self[IQ_BALANCE_PHA] )
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._sink.set_iq_balance( correction )
|
||||||
|
|
||||||
|
if self._verbose:
|
||||||
|
print "Set IQ balance to", correction
|
||||||
|
except RuntimeError as ex:
|
||||||
|
print ex
|
||||||
|
|
||||||
def set_freq(self, freq):
|
def set_freq(self, freq):
|
||||||
if freq is None:
|
if freq is None:
|
||||||
f = self[FREQ_RANGE_KEY]
|
f = self[FREQ_RANGE_KEY]
|
||||||
|
|
|
@ -149,6 +149,12 @@ class my_top_block(gr.top_block):
|
||||||
# build graph
|
# build graph
|
||||||
self.u = osmosdr.source_c(options.args)
|
self.u = osmosdr.source_c(options.args)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.u.get_sample_rates().start()
|
||||||
|
except RuntimeError:
|
||||||
|
print "Source has no sample rates (wrong device arguments?)."
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
# Set the antenna
|
# Set the antenna
|
||||||
if(options.antenna):
|
if(options.antenna):
|
||||||
self.u.set_antenna(options.antenna, 0)
|
self.u.set_antenna(options.antenna, 0)
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
INCLUDE(FindPkgConfig)
|
||||||
|
PKG_CHECK_MODULES(PC_GNURADIO_FCDPP gnuradio-fcdproplus)
|
||||||
|
|
||||||
|
FIND_PATH(
|
||||||
|
GNURADIO_FCDPP_INCLUDE_DIRS
|
||||||
|
NAMES fcdproplus/api.h
|
||||||
|
HINTS $ENV{GNURADIO_FCDPP_DIR}/include
|
||||||
|
${PC_GNURADIO_FCDPP_INCLUDEDIR}
|
||||||
|
PATHS /usr/local/include
|
||||||
|
/usr/include
|
||||||
|
)
|
||||||
|
|
||||||
|
FIND_LIBRARY(
|
||||||
|
GNURADIO_FCDPP_LIBRARIES
|
||||||
|
NAMES gnuradio-fcdproplus
|
||||||
|
HINTS $ENV{GNURADIO_FCDPP_DIR}/lib
|
||||||
|
${PC_GNURADIO_FCDPP_LIBDIR}
|
||||||
|
PATHS /usr/local/lib
|
||||||
|
/usr/local/lib64
|
||||||
|
/usr/lib
|
||||||
|
/usr/lib64
|
||||||
|
)
|
||||||
|
|
||||||
|
if(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES)
|
||||||
|
set(GNURADIO_FCDPP_FOUND TRUE CACHE INTERNAL "gnuradio-fcdproplus found")
|
||||||
|
message(STATUS "Found gnuradio-fcdproplus: ${GNURADIO_FCDPP_INCLUDE_DIRS}, ${GNURADIO_FCDPP_LIBRARIES}")
|
||||||
|
else(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES)
|
||||||
|
set(GNURADIO_FCDPP_FOUND FALSE CACHE INTERNAL "gnuradio-fcdproplus found")
|
||||||
|
message(STATUS "gnuradio-fcdproplus not found.")
|
||||||
|
endif(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES)
|
||||||
|
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_FCDPP DEFAULT_MSG GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS)
|
||||||
|
MARK_AS_ADVANCED(GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS)
|
|
@ -0,0 +1,24 @@
|
||||||
|
INCLUDE(FindPkgConfig)
|
||||||
|
PKG_CHECK_MODULES(PC_LIBAIRSPY libairspy)
|
||||||
|
|
||||||
|
FIND_PATH(
|
||||||
|
LIBAIRSPY_INCLUDE_DIRS
|
||||||
|
NAMES libairspy/airspy.h
|
||||||
|
HINTS $ENV{LIBAIRSPY_DIR}/include
|
||||||
|
${PC_LIBAIRSPY_INCLUDEDIR}
|
||||||
|
PATHS /usr/local/include
|
||||||
|
/usr/include
|
||||||
|
)
|
||||||
|
|
||||||
|
FIND_LIBRARY(
|
||||||
|
LIBAIRSPY_LIBRARIES
|
||||||
|
NAMES airspy
|
||||||
|
HINTS $ENV{LIBAIRSPY_DIR}/lib
|
||||||
|
${PC_LIBAIRSPY_LIBDIR}
|
||||||
|
PATHS /usr/local/lib
|
||||||
|
/usr/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBAIRSPY DEFAULT_MSG LIBAIRSPY_LIBRARIES LIBAIRSPY_INCLUDE_DIRS)
|
||||||
|
MARK_AS_ADVANCED(LIBAIRSPY_LIBRARIES LIBAIRSPY_INCLUDE_DIRS)
|
|
@ -1,6 +1,6 @@
|
||||||
if(NOT LIBMIRISDR_FOUND)
|
if(NOT LIBMIRISDR_FOUND)
|
||||||
pkg_check_modules (LIBMIRISDR_PKG libmirisdr)
|
pkg_check_modules (LIBMIRISDR_PKG libmirisdr)
|
||||||
find_path(LIBMIRISDR_INCLUDE_DIR NAMES mirisdr.h
|
find_path(LIBMIRISDR_INCLUDE_DIRS NAMES mirisdr.h
|
||||||
PATHS
|
PATHS
|
||||||
${LIBMIRISDR_PKG_INCLUDE_DIRS}
|
${LIBMIRISDR_PKG_INCLUDE_DIRS}
|
||||||
/usr/include
|
/usr/include
|
||||||
|
@ -14,14 +14,14 @@ if(NOT LIBMIRISDR_FOUND)
|
||||||
/usr/local/lib
|
/usr/local/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
if(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES)
|
if(LIBMIRISDR_INCLUDE_DIRS AND LIBMIRISDR_LIBRARIES)
|
||||||
set(LIBMIRISDR_FOUND TRUE CACHE INTERNAL "libmirisdr found")
|
set(LIBMIRISDR_FOUND TRUE CACHE INTERNAL "libmirisdr found")
|
||||||
message(STATUS "Found libmirisdr: ${LIBMIRISDR_INCLUDE_DIR}, ${LIBMIRISDR_LIBRARIES}")
|
message(STATUS "Found libmirisdr: ${LIBMIRISDR_INCLUDE_DIRS}, ${LIBMIRISDR_LIBRARIES}")
|
||||||
else(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES)
|
else(LIBMIRISDR_INCLUDE_DIRS AND LIBMIRISDR_LIBRARIES)
|
||||||
set(LIBMIRISDR_FOUND FALSE CACHE INTERNAL "libmirisdr found")
|
set(LIBMIRISDR_FOUND FALSE CACHE INTERNAL "libmirisdr found")
|
||||||
message(STATUS "libmirisdr not found.")
|
message(STATUS "libmirisdr not found.")
|
||||||
endif(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES)
|
endif(LIBMIRISDR_INCLUDE_DIRS AND LIBMIRISDR_LIBRARIES)
|
||||||
|
|
||||||
mark_as_advanced(LIBMIRISDR_INCLUDE_DIR LIBMIRISDR_LIBRARIES)
|
mark_as_advanced(LIBMIRISDR_LIBRARIES LIBMIRISDR_INCLUDE_DIRS)
|
||||||
|
|
||||||
endif(NOT LIBMIRISDR_FOUND)
|
endif(NOT LIBMIRISDR_FOUND)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
if(NOT LIBOSMOSDR_FOUND)
|
if(NOT LIBOSMOSDR_FOUND)
|
||||||
pkg_check_modules (LIBOSMOSDR_PKG libosmosdr)
|
pkg_check_modules (LIBOSMOSDR_PKG libosmosdr)
|
||||||
find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr.h
|
find_path(LIBOSMOSDR_INCLUDE_DIRS NAMES osmosdr.h
|
||||||
PATHS
|
PATHS
|
||||||
${LIBOSMOSDR_PKG_INCLUDE_DIRS}
|
${LIBOSMOSDR_PKG_INCLUDE_DIRS}
|
||||||
/usr/include
|
/usr/include
|
||||||
|
@ -14,14 +14,14 @@ if(NOT LIBOSMOSDR_FOUND)
|
||||||
/usr/local/lib
|
/usr/local/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
if(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
|
if(LIBOSMOSDR_INCLUDE_DIRS AND LIBOSMOSDR_LIBRARIES)
|
||||||
set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found")
|
set(LIBOSMOSDR_FOUND TRUE CACHE INTERNAL "libosmosdr found")
|
||||||
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}")
|
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIRS}, ${LIBOSMOSDR_LIBRARIES}")
|
||||||
else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
|
else(LIBOSMOSDR_INCLUDE_DIRS AND LIBOSMOSDR_LIBRARIES)
|
||||||
set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found")
|
set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr found")
|
||||||
message(STATUS "libosmosdr not found.")
|
message(STATUS "libosmosdr not found.")
|
||||||
endif(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
|
endif(LIBOSMOSDR_INCLUDE_DIRS AND LIBOSMOSDR_LIBRARIES)
|
||||||
|
|
||||||
mark_as_advanced(LIBOSMOSDR_INCLUDE_DIR LIBOSMOSDR_LIBRARIES)
|
mark_as_advanced(LIBOSMOSDR_LIBRARIES LIBOSMOSDR_INCLUDE_DIRS)
|
||||||
|
|
||||||
endif(NOT LIBOSMOSDR_FOUND)
|
endif(NOT LIBOSMOSDR_FOUND)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
if(NOT LIBRTLSDR_FOUND)
|
if(NOT LIBRTLSDR_FOUND)
|
||||||
pkg_check_modules (LIBRTLSDR_PKG librtlsdr)
|
pkg_check_modules (LIBRTLSDR_PKG librtlsdr)
|
||||||
find_path(LIBRTLSDR_INCLUDE_DIR NAMES rtl-sdr.h
|
find_path(LIBRTLSDR_INCLUDE_DIRS NAMES rtl-sdr.h
|
||||||
PATHS
|
PATHS
|
||||||
${LIBRTLSDR_PKG_INCLUDE_DIRS}
|
${LIBRTLSDR_PKG_INCLUDE_DIRS}
|
||||||
/usr/include
|
/usr/include
|
||||||
|
@ -14,14 +14,14 @@ if(NOT LIBRTLSDR_FOUND)
|
||||||
/usr/local/lib
|
/usr/local/lib
|
||||||
)
|
)
|
||||||
|
|
||||||
if(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
|
if(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES)
|
||||||
set(LIBRTLSDR_FOUND TRUE CACHE INTERNAL "librtlsdr found")
|
set(LIBRTLSDR_FOUND TRUE CACHE INTERNAL "librtlsdr found")
|
||||||
message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIR}, ${LIBRTLSDR_LIBRARIES}")
|
message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIRS}, ${LIBRTLSDR_LIBRARIES}")
|
||||||
else(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
|
else(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES)
|
||||||
set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr found")
|
set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr found")
|
||||||
message(STATUS "librtlsdr not found.")
|
message(STATUS "librtlsdr not found.")
|
||||||
endif(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
|
endif(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES)
|
||||||
|
|
||||||
mark_as_advanced(LIBRTLSDR_INCLUDE_DIR LIBRTLSDR_LIBRARIES)
|
mark_as_advanced(LIBRTLSDR_LIBRARIES LIBRTLSDR_INCLUDE_DIRS)
|
||||||
|
|
||||||
endif(NOT LIBRTLSDR_FOUND)
|
endif(NOT LIBRTLSDR_FOUND)
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
if(NOT LIBBLADERF_FOUND)
|
||||||
|
pkg_check_modules (LIBBLADERF_PKG libbladeRF)
|
||||||
|
find_path(LIBBLADERF_INCLUDE_DIRS NAMES libbladeRF.h
|
||||||
|
PATHS
|
||||||
|
${LIBBLADERF_PKG_INCLUDE_DIRS}
|
||||||
|
/usr/include
|
||||||
|
/usr/local/include
|
||||||
|
)
|
||||||
|
|
||||||
|
find_library(LIBBLADERF_LIBRARIES NAMES bladeRF
|
||||||
|
PATHS
|
||||||
|
${LIBBLADERF_PKG_LIBRARY_DIRS}
|
||||||
|
/usr/lib
|
||||||
|
/usr/local/lib
|
||||||
|
)
|
||||||
|
|
||||||
|
if(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
|
||||||
|
set(LIBBLADERF_FOUND TRUE CACHE INTERNAL "libbladeRF found")
|
||||||
|
message(STATUS "Found libbladeRF: ${LIBBLADERF_INCLUDE_DIRS}, ${LIBBLADERF_LIBRARIES}")
|
||||||
|
else(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
|
||||||
|
set(LIBBLADERF_FOUND FALSE CACHE INTERNAL "libbladeRF found")
|
||||||
|
message(STATUS "libbladeRF not found.")
|
||||||
|
endif(LIBBLADERF_INCLUDE_DIRS AND LIBBLADERF_LIBRARIES)
|
||||||
|
|
||||||
|
mark_as_advanced(LIBBLADERF_LIBRARIES LIBBLADERF_INCLUDE_DIRS)
|
||||||
|
|
||||||
|
endif(NOT LIBBLADERF_FOUND)
|
|
@ -36,11 +36,11 @@ if(PYTHON_EXECUTABLE)
|
||||||
else(PYTHON_EXECUTABLE)
|
else(PYTHON_EXECUTABLE)
|
||||||
|
|
||||||
#use the built-in find script
|
#use the built-in find script
|
||||||
find_package(PythonInterp)
|
find_package(PythonInterp 2)
|
||||||
|
|
||||||
#and if that fails use the find program routine
|
#and if that fails use the find program routine
|
||||||
if(NOT PYTHONINTERP_FOUND)
|
if(NOT PYTHONINTERP_FOUND)
|
||||||
find_program(PYTHON_EXECUTABLE NAMES python python2.7 python2.6 python2.5)
|
find_program(PYTHON_EXECUTABLE NAMES python python2 python2.7 python2.6 python2.5)
|
||||||
if(PYTHON_EXECUTABLE)
|
if(PYTHON_EXECUTABLE)
|
||||||
set(PYTHONINTERP_FOUND TRUE)
|
set(PYTHONINTERP_FOUND TRUE)
|
||||||
endif(PYTHON_EXECUTABLE)
|
endif(PYTHON_EXECUTABLE)
|
||||||
|
|
|
@ -115,7 +115,7 @@ macro(GR_SWIG_MAKE name)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#append additional include directories
|
#append additional include directories
|
||||||
find_package(PythonLibs)
|
find_package(PythonLibs 2)
|
||||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs)
|
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_PATH}) #deprecated name (now dirs)
|
||||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
|
list(APPEND GR_SWIG_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
|
||||||
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
|
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
|
@ -5,7 +5,7 @@ includedir=${prefix}/@GR_INCLUDE_DIR@
|
||||||
|
|
||||||
Name: @CPACK_PACKAGE_NAME@
|
Name: @CPACK_PACKAGE_NAME@
|
||||||
Description: @CPACK_PACKAGE_DESCRIPTION_SUMMARY@
|
Description: @CPACK_PACKAGE_DESCRIPTION_SUMMARY@
|
||||||
URL: http://sdr.osmocom.org/trac/
|
URL: http://sdr.osmocom.org/trac/wiki/GrOsmoSDR
|
||||||
Version: @CPACK_PACKAGE_VERSION@
|
Version: @CPACK_PACKAGE_VERSION@
|
||||||
Requires: gnuradio-core
|
Requires: gnuradio-core
|
||||||
Requires.private: @GR_OSMOSDR_PC_REQUIRES@
|
Requires.private: @GR_OSMOSDR_PC_REQUIRES@
|
||||||
|
|
|
@ -26,14 +26,17 @@ MAIN_TMPL = """\
|
||||||
<category>$($sourk.title())s</category>
|
<category>$($sourk.title())s</category>
|
||||||
<throttle>1</throttle>
|
<throttle>1</throttle>
|
||||||
<import>import osmosdr</import>
|
<import>import osmosdr</import>
|
||||||
<make>osmosdr.$(sourk)_c( args="nchan=" + str(\$nchan) + " " + \$args )
|
<make>osmosdr.$(sourk)_c( args="numchan=" + str(\$nchan) + " " + \$args )
|
||||||
self.\$(id).set_sample_rate(\$sample_rate)
|
self.\$(id).set_sample_rate(\$sample_rate)
|
||||||
#for $n in range($max_nchan)
|
#for $n in range($max_nchan)
|
||||||
\#if \$nchan() > $n
|
\#if \$nchan() > $n
|
||||||
self.\$(id).set_center_freq(\$freq$(n), $n)
|
self.\$(id).set_center_freq(\$freq$(n), $n)
|
||||||
self.\$(id).set_freq_corr(\$corr$(n), $n)
|
self.\$(id).set_freq_corr(\$corr$(n), $n)
|
||||||
|
#if $sourk == 'source':
|
||||||
|
self.\$(id).set_dc_offset_mode(\$dc_offset_mode$(n), $n)
|
||||||
self.\$(id).set_iq_balance_mode(\$iq_balance_mode$(n), $n)
|
self.\$(id).set_iq_balance_mode(\$iq_balance_mode$(n), $n)
|
||||||
self.\$(id).set_gain_mode(\$gain_mode$(n), $n)
|
self.\$(id).set_gain_mode(\$gain_mode$(n), $n)
|
||||||
|
#end if
|
||||||
self.\$(id).set_gain(\$gain$(n), $n)
|
self.\$(id).set_gain(\$gain$(n), $n)
|
||||||
self.\$(id).set_if_gain(\$if_gain$(n), $n)
|
self.\$(id).set_if_gain(\$if_gain$(n), $n)
|
||||||
self.\$(id).set_bb_gain(\$bb_gain$(n), $n)
|
self.\$(id).set_bb_gain(\$bb_gain$(n), $n)
|
||||||
|
@ -46,8 +49,11 @@ self.\$(id).set_bandwidth(\$bw$(n), $n)
|
||||||
#for $n in range($max_nchan)
|
#for $n in range($max_nchan)
|
||||||
<callback>set_center_freq(\$freq$(n), $n)</callback>
|
<callback>set_center_freq(\$freq$(n), $n)</callback>
|
||||||
<callback>set_freq_corr(\$corr$(n), $n)</callback>
|
<callback>set_freq_corr(\$corr$(n), $n)</callback>
|
||||||
|
#if $sourk == 'source':
|
||||||
|
<callback>set_dc_offset_mode(\$dc_offset_mode$(n), $n)</callback>
|
||||||
<callback>set_iq_balance_mode(\$iq_balance_mode$(n), $n)</callback>
|
<callback>set_iq_balance_mode(\$iq_balance_mode$(n), $n)</callback>
|
||||||
<callback>set_gain_mode(\$gain_mode$(n), $n)</callback>
|
<callback>set_gain_mode(\$gain_mode$(n), $n)</callback>
|
||||||
|
#end if
|
||||||
<callback>set_gain(\$gain$(n), $n)</callback>
|
<callback>set_gain(\$gain$(n), $n)</callback>
|
||||||
<callback>set_if_gain(\$if_gain$(n), $n)</callback>
|
<callback>set_if_gain(\$if_gain$(n), $n)</callback>
|
||||||
<callback>set_bb_gain(\$bb_gain$(n), $n)</callback>
|
<callback>set_bb_gain(\$bb_gain$(n), $n)</callback>
|
||||||
|
@ -104,20 +110,27 @@ self.\$(id).set_bandwidth(\$bw$(n), $n)
|
||||||
<nports>\$nchan</nports>
|
<nports>\$nchan</nports>
|
||||||
</$sourk>
|
</$sourk>
|
||||||
<doc>
|
<doc>
|
||||||
The OSMOCOM block:
|
The osmocom $sourk block:
|
||||||
|
|
||||||
While primarily being developed for the OsmoSDR hardware, this block as well supports:
|
While primarily being developed for the OsmoSDR hardware, this block as well supports:
|
||||||
|
|
||||||
* FunCube Dongle through libgnuradio-fcd
|
#if $sourk == 'source':
|
||||||
* OSMOCOM OsmoSDR Devices through libosmosdr
|
* FUNcube Dongle through libgnuradio-fcd
|
||||||
* Great Scott Gadgets HackRF through libhackrf
|
* FUNcube Dongle Pro+ through gr-fcdproplus
|
||||||
* Ettus USRP Devices through Ettus UHD library
|
* sysmocom OsmoSDR Devices through libosmosdr
|
||||||
* RTL2832U based DVB-T dongles through librtlsdr
|
* RTL2832U based DVB-T dongles through librtlsdr
|
||||||
* RTL-TCP spectrum server (see librtlsdr project)
|
* RTL-TCP spectrum server (see librtlsdr project)
|
||||||
* MSi2500 based DVB-T dongles through libmirisdr
|
* MSi2500 based DVB-T dongles through libmirisdr
|
||||||
* gnuradio .cfile input through libgnuradio-core
|
* gnuradio .cfile input through libgnuradio-blocks
|
||||||
|
* RFSPACE SDR-IQ, SDR-IP, NetSDR (incl. X2 option)
|
||||||
|
* AirSpy Wideband Receiver through libairspy
|
||||||
|
#end if
|
||||||
|
* Great Scott Gadgets HackRF through libhackrf
|
||||||
|
* Nuand LLC bladeRF through libbladeRF library
|
||||||
|
* Ettus USRP Devices through Ettus UHD library
|
||||||
|
* Fairwaves UmTRX through Fairwaves' fork of UHD
|
||||||
|
|
||||||
By using the OsmoSDR block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
|
By using the osmocom $sourk block you can take advantage of a common software api in your application(s) independent of the underlying radio hardware.
|
||||||
|
|
||||||
Output Type:
|
Output Type:
|
||||||
This parameter controls the data type of the stream in gnuradio. Only complex float32 samples are supported at the moment.
|
This parameter controls the data type of the stream in gnuradio. Only complex float32 samples are supported at the moment.
|
||||||
|
@ -131,21 +144,23 @@ Examples:
|
||||||
Optional arguments are placed into [] brackets, remove the brackets before using them! Specific variable values are separated with a |, choose one of them. Variable values containing spaces shall be enclosed in '' as demonstrated in examples section below.
|
Optional arguments are placed into [] brackets, remove the brackets before using them! Specific variable values are separated with a |, choose one of them. Variable values containing spaces shall be enclosed in '' as demonstrated in examples section below.
|
||||||
Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space.
|
Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space.
|
||||||
|
|
||||||
Source Mode:
|
#if $sourk == 'source':
|
||||||
fcd=0
|
fcd=0[,device=hw:2][,type=2]
|
||||||
hackrf=0[,buffers=32]
|
|
||||||
miri=0[,buffers=32] ...
|
miri=0[,buffers=32] ...
|
||||||
rtl=serial_number ...
|
rtl=serial_number ...
|
||||||
rtl=0[,rtl_xtal=28.8e6][,tuner_xtal=28.8e6] ...
|
rtl=0[,rtl_xtal=28.8e6][,tuner_xtal=28.8e6] ...
|
||||||
rtl=1[,buffers=32][,buflen=N*512] ...
|
rtl=1[,buffers=32][,buflen=N*512] ...
|
||||||
rtl=2[,direct_samp=0|1|2][,offset_tune=0|1] ...
|
rtl=2[,direct_samp=0|1|2][,offset_tune=0|1] ...
|
||||||
rtl_tcp=127.0.0.1:1234[,psize=16384][,direct_samp=0|1|2][,offset_tune=0|1] ...
|
rtl_tcp=127.0.0.1:1234[,psize=16384][,direct_samp=0|1|2][,offset_tune=0|1] ...
|
||||||
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
|
|
||||||
osmosdr=0[,buffers=32][,buflen=N*512] ...
|
osmosdr=0[,buffers=32][,buflen=N*512] ...
|
||||||
file='/path/to/your file',rate=1e6[,freq=100e6][,repeat=true][,throttle=true] ...
|
file='/path/to/your file',rate=1e6[,freq=100e6][,repeat=true][,throttle=true] ...
|
||||||
|
netsdr=127.0.0.1[:50000][,nchan=2]
|
||||||
Sink Mode:
|
sdr-ip=127.0.0.1[:50000]
|
||||||
|
sdr-iq=/dev/ttyUSB0
|
||||||
|
airspy=0
|
||||||
|
#end if
|
||||||
hackrf=0[,buffers=32]
|
hackrf=0[,buffers=32]
|
||||||
|
bladerf=0[,fpga='/path/to/the/bitstream.rbf']
|
||||||
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
|
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
|
||||||
|
|
||||||
Num Channels:
|
Num Channels:
|
||||||
|
@ -160,6 +175,15 @@ The center frequency is the frequency the RF chain is tuned to.
|
||||||
Freq. Corr.:
|
Freq. Corr.:
|
||||||
The frequency correction factor in parts per million (ppm). Set to 0 if unknown.
|
The frequency correction factor in parts per million (ppm). Set to 0 if unknown.
|
||||||
|
|
||||||
|
#if $sourk == 'source':
|
||||||
|
DC Offset Mode:
|
||||||
|
Controls the behavior of hardware DC offset corrrection.
|
||||||
|
Off: Disable correction algorithm (pass through).
|
||||||
|
Manual: Keep last estimated correction when switched from Automatic to Manual.
|
||||||
|
Automatic: Periodicallly find the best solution to compensate for DC offset.
|
||||||
|
|
||||||
|
This functionality is available for USRP devices only.
|
||||||
|
|
||||||
IQ Balance Mode:
|
IQ Balance Mode:
|
||||||
Controls the behavior of software IQ imbalance corrrection.
|
Controls the behavior of software IQ imbalance corrrection.
|
||||||
Off: Disable correction algorithm (pass through).
|
Off: Disable correction algorithm (pass through).
|
||||||
|
@ -173,16 +197,17 @@ Chooses between the manual (default) and automatic gain mode where appropriate.
|
||||||
To allow manual control of RF/IF/BB gain stages, manual gain mode must be configured.
|
To allow manual control of RF/IF/BB gain stages, manual gain mode must be configured.
|
||||||
Currently, only RTL-SDR devices support automatic gain mode.
|
Currently, only RTL-SDR devices support automatic gain mode.
|
||||||
|
|
||||||
|
#end if
|
||||||
RF Gain:
|
RF Gain:
|
||||||
Overall RF gain of the receiving device.
|
Overall RF gain of the device.
|
||||||
|
|
||||||
IF Gain:
|
IF Gain:
|
||||||
Overall intermediate frequency gain of the receiving device.
|
Overall intermediate frequency gain of the device.
|
||||||
This setting has only effect for RTL-SDR and OsmoSDR devices with E4000 tuners. Observations lead to a reasonable gain range from 15 to 30dB.
|
This setting is available for RTL-SDR and OsmoSDR devices with E4000 tuners and HackRF Jawbreaker in receive and transmit mode. Observations lead to a reasonable gain range from 15 to 30dB.
|
||||||
|
|
||||||
BB Gain:
|
BB Gain:
|
||||||
Overall baseband gain of the receiving device.
|
Overall baseband gain of the device.
|
||||||
This setting has only effect for HackRF Jawbreaker. Observations lead to a reasonable gain range from 15 to 30dB.
|
This setting is available for HackRF Jawbreaker in receive mode. Observations lead to a reasonable gain range from 15 to 30dB.
|
||||||
|
|
||||||
Antenna:
|
Antenna:
|
||||||
For devices with only one antenna, this may be left blank.
|
For devices with only one antenna, this may be left blank.
|
||||||
|
@ -192,9 +217,9 @@ Bandwidth:
|
||||||
Set the bandpass filter on the radio frontend. To use the default (automatic) bandwidth filter setting, this should be zero.
|
Set the bandpass filter on the radio frontend. To use the default (automatic) bandwidth filter setting, this should be zero.
|
||||||
|
|
||||||
See the OsmoSDR project page for more detailed documentation:
|
See the OsmoSDR project page for more detailed documentation:
|
||||||
http://sdr.osmocom.org/trac/
|
|
||||||
http://sdr.osmocom.org/trac/wiki/rtl-sdr
|
|
||||||
http://sdr.osmocom.org/trac/wiki/GrOsmoSDR
|
http://sdr.osmocom.org/trac/wiki/GrOsmoSDR
|
||||||
|
http://sdr.osmocom.org/trac/wiki/rtl-sdr
|
||||||
|
http://sdr.osmocom.org/trac/
|
||||||
</doc>
|
</doc>
|
||||||
</block>
|
</block>
|
||||||
"""
|
"""
|
||||||
|
@ -214,6 +239,26 @@ PARAMS_TMPL = """
|
||||||
<type>real</type>
|
<type>real</type>
|
||||||
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
|
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
|
||||||
</param>
|
</param>
|
||||||
|
#if $sourk == 'source':
|
||||||
|
<param>
|
||||||
|
<name>Ch$(n): DC Offset Mode</name>
|
||||||
|
<key>dc_offset_mode$(n)</key>
|
||||||
|
<value>0</value>
|
||||||
|
<type>int</type>
|
||||||
|
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
|
||||||
|
<option>
|
||||||
|
<name>Off</name>
|
||||||
|
<key>0</key>
|
||||||
|
</option>
|
||||||
|
<option>
|
||||||
|
<name>Manual</name>
|
||||||
|
<key>1</key>
|
||||||
|
</option>
|
||||||
|
<option>
|
||||||
|
<name>Automatic</name>
|
||||||
|
<key>2</key>
|
||||||
|
</option>
|
||||||
|
</param>
|
||||||
<param>
|
<param>
|
||||||
<name>Ch$(n): IQ Balance Mode</name>
|
<name>Ch$(n): IQ Balance Mode</name>
|
||||||
<key>iq_balance_mode$(n)</key>
|
<key>iq_balance_mode$(n)</key>
|
||||||
|
@ -248,6 +293,7 @@ PARAMS_TMPL = """
|
||||||
<key>1</key>
|
<key>1</key>
|
||||||
</option>
|
</option>
|
||||||
</param>
|
</param>
|
||||||
|
#end if
|
||||||
<param>
|
<param>
|
||||||
<name>Ch$(n): RF Gain (dB)</name>
|
<name>Ch$(n): RF Gain (dB)</name>
|
||||||
<key>gain$(n)</key>
|
<key>gain$(n)</key>
|
||||||
|
@ -318,7 +364,7 @@ if __name__ == '__main__':
|
||||||
title = 'RTL-SDR'
|
title = 'RTL-SDR'
|
||||||
prefix = 'rtlsdr'
|
prefix = 'rtlsdr'
|
||||||
elif tail.startswith('osmosdr'):
|
elif tail.startswith('osmosdr'):
|
||||||
title = 'OSMOCOM'
|
title = 'osmocom'
|
||||||
prefix = 'osmosdr'
|
prefix = 'osmosdr'
|
||||||
else: raise Exception, 'file %s has wrong syntax!'%tail
|
else: raise Exception, 'file %s has wrong syntax!'%tail
|
||||||
|
|
||||||
|
@ -330,7 +376,7 @@ if __name__ == '__main__':
|
||||||
dir = 'in'
|
dir = 'in'
|
||||||
else: raise Exception, 'is %s a source or sink?'%file
|
else: raise Exception, 'is %s a source or sink?'%file
|
||||||
|
|
||||||
params = ''.join([parse_tmpl(PARAMS_TMPL, n=n) for n in range(max_num_channels)])
|
params = ''.join([parse_tmpl(PARAMS_TMPL, n=n, sourk=sourk) for n in range(max_num_channels)])
|
||||||
open(file, 'w').write(parse_tmpl(MAIN_TMPL,
|
open(file, 'w').write(parse_tmpl(MAIN_TMPL,
|
||||||
max_nchan=max_num_channels,
|
max_nchan=max_num_channels,
|
||||||
params=params,
|
params=params,
|
||||||
|
|
|
@ -101,6 +101,9 @@ namespace osmosdr {
|
||||||
* The device hint should be used to narrow down the search
|
* The device hint should be used to narrow down the search
|
||||||
* to particular transport types and/or transport arguments.
|
* to particular transport types and/or transport arguments.
|
||||||
*
|
*
|
||||||
|
* The device hint "nofake" switches off dummy devices created
|
||||||
|
* by "file" (and other) implementations.
|
||||||
|
*
|
||||||
* \param hint a partially (or fully) filled in logical device
|
* \param hint a partially (or fully) filled in logical device
|
||||||
* \return a vector of logical devices for all radios on the system
|
* \return a vector of logical devices for all radios on the system
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -240,33 +240,27 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
enum IQBalanceMode {
|
|
||||||
IQBalanceOff = 0,
|
|
||||||
IQBalanceManual,
|
|
||||||
IQBalanceAutomatic
|
|
||||||
};
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the RX frontend IQ balance mode.
|
* Set the TX frontend DC offset value.
|
||||||
|
* The value is complex to control both I and Q.
|
||||||
*
|
*
|
||||||
* \param mode iq balance correction mode: 0 = Off, 1 = Manual, 2 = Automatic
|
* \param offset the dc offset (1.0 is full-scale)
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance_mode( int mode, size_t chan = 0 ) = 0;
|
virtual void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the RX frontend IQ balance correction.
|
* Set the TX frontend IQ balance correction.
|
||||||
* Use this to adjust the magnitude and phase of I and Q.
|
* Use this to adjust the magnitude and phase of I and Q.
|
||||||
*
|
*
|
||||||
* \param correction the complex correction value
|
* \param balance the complex correction value
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance( const std::complex<double> &correction,
|
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) = 0;
|
||||||
size_t chan = 0 ) = 0;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the bandpass filter on the radio frontend.
|
* Set the bandpass filter on the radio frontend.
|
||||||
* \param bandwidth the filter bandwidth in Hz
|
* \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
* \return the actual filter bandwidth in Hz
|
* \return the actual filter bandwidth in Hz
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -63,6 +63,15 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual size_t get_num_channels( void ) = 0;
|
virtual size_t get_num_channels( void ) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief seek file to \p seek_point relative to \p whence
|
||||||
|
*
|
||||||
|
* \param seek_point sample offset in file
|
||||||
|
* \param whence one of SEEK_SET, SEEK_CUR, SEEK_END (man fseek)
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
virtual bool seek( long seek_point, int whence, size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Get the possible sample rates for the underlying radio hardware.
|
* Get the possible sample rates for the underlying radio hardware.
|
||||||
* \return a range of rates in Sps
|
* \return a range of rates in Sps
|
||||||
|
@ -240,6 +249,36 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
|
enum DCOffsetMode {
|
||||||
|
DCOffsetOff = 0,
|
||||||
|
DCOffsetManual,
|
||||||
|
DCOffsetAutomatic
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the RX frontend DC correction mode.
|
||||||
|
* The automatic correction subtracts out the long-run average.
|
||||||
|
*
|
||||||
|
* When disabled, the averaging option operation is reset.
|
||||||
|
* Once in Manual mode, the average value will be held constant until
|
||||||
|
* the user re-enables the automatic correction or overrides the
|
||||||
|
* value by manually setting the offset.
|
||||||
|
*
|
||||||
|
* \param mode dc offset correction mode: 0 = Off, 1 = Manual, 2 = Automatic
|
||||||
|
* \param chan the channel index 0 to N-1
|
||||||
|
*/
|
||||||
|
virtual void set_dc_offset_mode( int mode, size_t chan = 0) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the RX frontend DC offset value.
|
||||||
|
* The value is complex to control both I and Q.
|
||||||
|
* Only set this when automatic correction is disabled.
|
||||||
|
*
|
||||||
|
* \param offset the dc offset (1.0 is full-scale)
|
||||||
|
* \param chan the channel index 0 to N-1
|
||||||
|
*/
|
||||||
|
virtual void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
enum IQBalanceMode {
|
enum IQBalanceMode {
|
||||||
IQBalanceOff = 0,
|
IQBalanceOff = 0,
|
||||||
IQBalanceManual,
|
IQBalanceManual,
|
||||||
|
@ -258,15 +297,14 @@ public:
|
||||||
* Set the RX frontend IQ balance correction.
|
* Set the RX frontend IQ balance correction.
|
||||||
* Use this to adjust the magnitude and phase of I and Q.
|
* Use this to adjust the magnitude and phase of I and Q.
|
||||||
*
|
*
|
||||||
* \param correction the complex correction value
|
* \param balance the complex correction value
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance( const std::complex<double> &correction,
|
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) = 0;
|
||||||
size_t chan = 0 ) = 0;
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the bandpass filter on the radio frontend.
|
* Set the bandpass filter on the radio frontend.
|
||||||
* \param bandwidth the filter bandwidth in Hz
|
* \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
* \return the actual filter bandwidth in Hz
|
* \return the actual filter bandwidth in Hz
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -69,10 +69,17 @@ endif(ENABLE_OSMOSDR)
|
||||||
########################################################################
|
########################################################################
|
||||||
# Setup FCD component
|
# Setup FCD component
|
||||||
########################################################################
|
########################################################################
|
||||||
GR_REGISTER_COMPONENT("FunCube Dongle" ENABLE_FCD GNURADIO_FCD_FOUND)
|
GR_REGISTER_COMPONENT("FUNcube Dongle" ENABLE_FCD GNURADIO_FCD_FOUND)
|
||||||
|
GR_REGISTER_COMPONENT("FUNcube Dongle Pro+" ENABLE_FCDPP GNURADIO_FCDPP_FOUND)
|
||||||
if(ENABLE_FCD)
|
if(ENABLE_FCD)
|
||||||
GR_INCLUDE_SUBDIRECTORY(fcd)
|
add_definitions(-DHAVE_FCD=1)
|
||||||
endif(ENABLE_FCD)
|
endif(ENABLE_FCD)
|
||||||
|
if(ENABLE_FCDPP)
|
||||||
|
add_definitions(-DHAVE_FCDPP=1)
|
||||||
|
endif(ENABLE_FCDPP)
|
||||||
|
if(ENABLE_FCD OR ENABLE_FCDPP)
|
||||||
|
GR_INCLUDE_SUBDIRECTORY(fcd)
|
||||||
|
endif(ENABLE_FCD OR ENABLE_FCDPP)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Setup File component
|
# Setup File component
|
||||||
|
@ -122,6 +129,30 @@ if(ENABLE_HACKRF)
|
||||||
GR_INCLUDE_SUBDIRECTORY(hackrf)
|
GR_INCLUDE_SUBDIRECTORY(hackrf)
|
||||||
endif(ENABLE_HACKRF)
|
endif(ENABLE_HACKRF)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Setup bladeRF component
|
||||||
|
########################################################################
|
||||||
|
GR_REGISTER_COMPONENT("nuand bladeRF" ENABLE_BLADERF LIBBLADERF_FOUND)
|
||||||
|
if(ENABLE_BLADERF)
|
||||||
|
GR_INCLUDE_SUBDIRECTORY(bladerf)
|
||||||
|
endif(ENABLE_BLADERF)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Setup RFSPACE component
|
||||||
|
########################################################################
|
||||||
|
GR_REGISTER_COMPONENT("RFSPACE Receivers" ENABLE_RFSPACE)
|
||||||
|
if(ENABLE_RFSPACE)
|
||||||
|
GR_INCLUDE_SUBDIRECTORY(rfspace)
|
||||||
|
endif(ENABLE_RFSPACE)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Setup AIRSPY component
|
||||||
|
########################################################################
|
||||||
|
GR_REGISTER_COMPONENT("AIRSPY Receiver" ENABLE_AIRSPY LIBAIRSPY_FOUND)
|
||||||
|
if(ENABLE_AIRSPY)
|
||||||
|
GR_INCLUDE_SUBDIRECTORY(airspy)
|
||||||
|
endif(ENABLE_AIRSPY)
|
||||||
|
|
||||||
########################################################################
|
########################################################################
|
||||||
# Setup configuration file
|
# Setup configuration file
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright 2012 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This file is part of GNU Radio
|
||||||
|
#
|
||||||
|
# GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# This file included, use CMake directory variables
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${LIBAIRSPY_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(airspy_srcs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/airspy_source_c.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Append gnuradio-osmosdr library sources
|
||||||
|
########################################################################
|
||||||
|
list(APPEND gr_osmosdr_srcs ${airspy_srcs})
|
||||||
|
list(APPEND gr_osmosdr_libs ${LIBAIRSPY_LIBRARIES} ${GNURADIO_FILTER_LIBRARIES} ${GNURADIO_BLOCKS_LIBRARIES})
|
|
@ -0,0 +1,609 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config.h is generated by configure. It contains the results
|
||||||
|
* of probing for features, options etc. It should be the first
|
||||||
|
* file included in your .cc file.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/detail/endian.hpp>
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/thread/thread.hpp>
|
||||||
|
|
||||||
|
#include <gnuradio/gr_io_signature.h>
|
||||||
|
|
||||||
|
#include "airspy_source_c.h"
|
||||||
|
|
||||||
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
|
||||||
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
#define AIRSPY_THROW_ON_ERROR(ret, msg) \
|
||||||
|
if ( ret != AIRSPY_SUCCESS ) \
|
||||||
|
throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
|
||||||
|
% ret % airspy_error_name((enum airspy_error)ret) ) );
|
||||||
|
|
||||||
|
#define AIRSPY_FUNC_STR(func, arg) \
|
||||||
|
boost::str(boost::format(func "(%d)") % arg) + " has failed"
|
||||||
|
|
||||||
|
int airspy_source_c::_usage = 0;
|
||||||
|
boost::mutex airspy_source_c::_usage_mutex;
|
||||||
|
|
||||||
|
airspy_source_c_sptr make_airspy_source_c (const std::string & args)
|
||||||
|
{
|
||||||
|
return gnuradio::get_initial_sptr(new airspy_source_c (args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify constraints on number of input and output streams.
|
||||||
|
* This info is used to construct the input and output signatures
|
||||||
|
* (2nd & 3rd args to gr_block's constructor). The input and
|
||||||
|
* output signatures are used by the runtime system to
|
||||||
|
* check that a valid number and type of inputs and outputs
|
||||||
|
* are connected to this block. In this case, we accept
|
||||||
|
* only 0 input and 1 output.
|
||||||
|
*/
|
||||||
|
static const int MIN_IN = 0; // mininum number of input streams
|
||||||
|
static const int MAX_IN = 0; // maximum number of input streams
|
||||||
|
static const int MIN_OUT = 1; // minimum number of output streams
|
||||||
|
static const int MAX_OUT = 1; // maximum number of output streams
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The private constructor
|
||||||
|
*/
|
||||||
|
airspy_source_c::airspy_source_c (const std::string &args)
|
||||||
|
: gr_sync_block ("airspy_source_c",
|
||||||
|
gr_make_io_signature(MIN_IN, MAX_IN, sizeof (gr_complex)),
|
||||||
|
gr_make_io_signature(MIN_OUT, MAX_OUT, sizeof (gr_complex))),
|
||||||
|
_dev(NULL),
|
||||||
|
_sample_rate(0),
|
||||||
|
_center_freq(0),
|
||||||
|
_freq_corr(0),
|
||||||
|
_auto_gain(false),
|
||||||
|
_lna_gain(0),
|
||||||
|
_mix_gain(0),
|
||||||
|
_vga_gain(0),
|
||||||
|
_bandwidth(0)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
airspy_init(); /* call only once before the first open */
|
||||||
|
|
||||||
|
_usage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_dev = NULL;
|
||||||
|
ret = airspy_open( &_dev );
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to open AirSpy device")
|
||||||
|
|
||||||
|
uint8_t board_id;
|
||||||
|
ret = airspy_board_id_read( _dev, &board_id );
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to get AirSpy board id")
|
||||||
|
|
||||||
|
char version[40];
|
||||||
|
memset(version, 0, sizeof(version));
|
||||||
|
ret = airspy_version_string_read( _dev, version, sizeof(version));
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to read version string")
|
||||||
|
#if 0
|
||||||
|
read_partid_serialno_t serial_number;
|
||||||
|
ret = airspy_board_partid_serialno_read( _dev, &serial_number );
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to read serial number")
|
||||||
|
#endif
|
||||||
|
std::cerr << "Using " << airspy_board_id_name(airspy_board_id(board_id)) << " "
|
||||||
|
<< "with firmware " << version << " "
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
|
||||||
|
set_sample_rate( get_sample_rates().start() );
|
||||||
|
set_bandwidth( 0 );
|
||||||
|
|
||||||
|
set_gain( 8 ); /* preset to a reasonable default (non-GRC use case) */
|
||||||
|
|
||||||
|
set_mix_gain( 5 ); /* preset to a reasonable default (non-GRC use case) */
|
||||||
|
|
||||||
|
set_if_gain( 0 ); /* preset to a reasonable default (non-GRC use case) */
|
||||||
|
|
||||||
|
_fifo = new boost::circular_buffer<gr_complex>(5000000);
|
||||||
|
if (!_fifo) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to allocate a sample FIFO!" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Our virtual destructor.
|
||||||
|
*/
|
||||||
|
airspy_source_c::~airspy_source_c ()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
if ( airspy_is_streaming( _dev ) == AIRSPY_TRUE )
|
||||||
|
{
|
||||||
|
ret = airspy_stop_rx( _dev );
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to stop RX streaming")
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = airspy_close( _dev );
|
||||||
|
AIRSPY_THROW_ON_ERROR(ret, "Failed to close AirSpy")
|
||||||
|
_dev = NULL;
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
_usage--;
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
airspy_exit(); /* call only once after last close */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_fifo)
|
||||||
|
{
|
||||||
|
delete _fifo;
|
||||||
|
_fifo = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int airspy_source_c::_airspy_rx_callback(airspy_transfer *transfer)
|
||||||
|
{
|
||||||
|
airspy_source_c *obj = (airspy_source_c *)transfer->ctx;
|
||||||
|
|
||||||
|
return obj->airspy_rx_callback((float *)transfer->samples, transfer->sample_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
int airspy_source_c::airspy_rx_callback(void *samples, int sample_count)
|
||||||
|
{
|
||||||
|
size_t i, n_avail, to_copy, num_samples = sample_count;
|
||||||
|
float *sample = (float *)samples;
|
||||||
|
|
||||||
|
_fifo_lock.lock();
|
||||||
|
|
||||||
|
n_avail = _fifo->capacity() - _fifo->size();
|
||||||
|
to_copy = (n_avail < num_samples ? n_avail : num_samples);
|
||||||
|
|
||||||
|
for (i = 0; i < to_copy; i++ )
|
||||||
|
{
|
||||||
|
/* Push sample to the fifo */
|
||||||
|
_fifo->push_back( gr_complex( *sample, *(sample+1) ) );
|
||||||
|
|
||||||
|
/* offset to the next I+Q sample */
|
||||||
|
sample += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fifo_lock.unlock();
|
||||||
|
|
||||||
|
/* We have made some new samples available to the consumer in work() */
|
||||||
|
if (to_copy) {
|
||||||
|
//std::cerr << "+" << std::flush;
|
||||||
|
_samp_avail.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Indicate overrun, if neccesary */
|
||||||
|
if (to_copy < num_samples)
|
||||||
|
std::cerr << "O" << std::flush;
|
||||||
|
|
||||||
|
return 0; // TODO: return -1 on error/stop
|
||||||
|
}
|
||||||
|
|
||||||
|
bool airspy_source_c::start()
|
||||||
|
{
|
||||||
|
if ( ! _dev )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int ret = airspy_start_rx( _dev, _airspy_rx_callback, (void *)this );
|
||||||
|
if ( ret != AIRSPY_SUCCESS ) {
|
||||||
|
std::cerr << "Failed to start RX streaming (" << ret << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool airspy_source_c::stop()
|
||||||
|
{
|
||||||
|
if ( ! _dev )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int ret = airspy_stop_rx( _dev );
|
||||||
|
if ( ret != AIRSPY_SUCCESS ) {
|
||||||
|
std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int airspy_source_c::work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items )
|
||||||
|
{
|
||||||
|
gr_complex *out = (gr_complex *)output_items[0];
|
||||||
|
|
||||||
|
bool running = false;
|
||||||
|
|
||||||
|
if ( _dev )
|
||||||
|
running = (airspy_is_streaming( _dev ) == AIRSPY_TRUE);
|
||||||
|
|
||||||
|
if ( ! running )
|
||||||
|
return WORK_DONE;
|
||||||
|
|
||||||
|
boost::unique_lock<boost::mutex> lock(_fifo_lock);
|
||||||
|
|
||||||
|
/* Wait until we have the requested number of samples */
|
||||||
|
int n_samples_avail = _fifo->size();
|
||||||
|
|
||||||
|
while (n_samples_avail < noutput_items) {
|
||||||
|
_samp_avail.wait(lock);
|
||||||
|
n_samples_avail = _fifo->size();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < noutput_items; ++i) {
|
||||||
|
out[i] = _fifo->at(0);
|
||||||
|
_fifo->pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cerr << "-" << std::flush;
|
||||||
|
|
||||||
|
return noutput_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> airspy_source_c::get_devices()
|
||||||
|
{
|
||||||
|
std::vector<std::string> devices;
|
||||||
|
std::string label;
|
||||||
|
#if 0
|
||||||
|
for (unsigned int i = 0; i < 1 /* TODO: missing libairspy api */; i++) {
|
||||||
|
std::string args = "airspy=" + boost::lexical_cast< std::string >( i );
|
||||||
|
|
||||||
|
label.clear();
|
||||||
|
|
||||||
|
label = "AirSpy"; /* TODO: missing libairspy api */
|
||||||
|
|
||||||
|
boost::algorithm::trim(label);
|
||||||
|
|
||||||
|
args += ",label='" + label + "'";
|
||||||
|
devices.push_back( args );
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
airspy_init(); /* call only once before the first open */
|
||||||
|
|
||||||
|
_usage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
airspy_device *dev = NULL;
|
||||||
|
ret = airspy_open(&dev);
|
||||||
|
if ( AIRSPY_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
std::string args = "airspy=0";
|
||||||
|
|
||||||
|
label = "AirSpy";
|
||||||
|
|
||||||
|
uint8_t board_id;
|
||||||
|
ret = airspy_board_id_read( dev, &board_id );
|
||||||
|
if ( AIRSPY_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
label += std::string(" ") + airspy_board_id_name(airspy_board_id(board_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
args += ",label='" + label + "'";
|
||||||
|
devices.push_back( args );
|
||||||
|
|
||||||
|
ret = airspy_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
_usage--;
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
airspy_exit(); /* call only once after last close */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t airspy_source_c::get_num_channels()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::meta_range_t airspy_source_c::get_sample_rates()
|
||||||
|
{
|
||||||
|
osmosdr::meta_range_t range;
|
||||||
|
|
||||||
|
range += osmosdr::range_t( 10e6 );
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_sample_rate( double rate )
|
||||||
|
{
|
||||||
|
int ret = AIRSPY_SUCCESS;
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
// ret = airspy_set_sample_rate( _dev, rate );
|
||||||
|
if ( AIRSPY_SUCCESS == ret ) {
|
||||||
|
//_sample_rate = rate;
|
||||||
|
_sample_rate = get_sample_rates().start();
|
||||||
|
} else {
|
||||||
|
AIRSPY_THROW_ON_ERROR( ret, AIRSPY_FUNC_STR( "airspy_set_sample_rate", rate ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_sample_rate();
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_sample_rate()
|
||||||
|
{
|
||||||
|
return _sample_rate;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t airspy_source_c::get_freq_range( size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::freq_range_t range;
|
||||||
|
|
||||||
|
range += osmosdr::range_t( 24e6, 1766e6 );
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_center_freq( double freq, size_t chan )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
#define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001))
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
double corr_freq = APPLY_PPM_CORR( freq, _freq_corr );
|
||||||
|
ret = airspy_set_freq( _dev, uint64_t(corr_freq) );
|
||||||
|
if ( AIRSPY_SUCCESS == ret ) {
|
||||||
|
_center_freq = freq;
|
||||||
|
} else {
|
||||||
|
AIRSPY_THROW_ON_ERROR( ret, AIRSPY_FUNC_STR( "airspy_set_freq", corr_freq ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_center_freq( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_center_freq( size_t chan )
|
||||||
|
{
|
||||||
|
return _center_freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_freq_corr( double ppm, size_t chan )
|
||||||
|
{
|
||||||
|
_freq_corr = ppm;
|
||||||
|
|
||||||
|
set_center_freq( _center_freq );
|
||||||
|
|
||||||
|
return get_freq_corr( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_freq_corr( size_t chan )
|
||||||
|
{
|
||||||
|
return _freq_corr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> airspy_source_c::get_gain_names( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > names;
|
||||||
|
|
||||||
|
names += "LNA";
|
||||||
|
names += "MIX";
|
||||||
|
names += "IF";
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t airspy_source_c::get_gain_range( size_t chan )
|
||||||
|
{
|
||||||
|
return get_gain_range( "LNA", chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t airspy_source_c::get_gain_range( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name ) {
|
||||||
|
return osmosdr::gain_range_t( 0, 15, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "MIX" == name ) {
|
||||||
|
return osmosdr::gain_range_t( 0, 15, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "IF" == name ) {
|
||||||
|
return osmosdr::gain_range_t( 0, 15, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
return osmosdr::gain_range_t();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool airspy_source_c::set_gain_mode( bool automatic, size_t chan )
|
||||||
|
{
|
||||||
|
_auto_gain = automatic;
|
||||||
|
|
||||||
|
return get_gain_mode(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool airspy_source_c::get_gain_mode( size_t chan )
|
||||||
|
{
|
||||||
|
return _auto_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_gain( double gain, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = AIRSPY_SUCCESS;
|
||||||
|
osmosdr::gain_range_t gains = get_gain_range( "LNA", chan );
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
double clip_gain = gains.clip( gain, true );
|
||||||
|
uint8_t value = clip_gain;
|
||||||
|
|
||||||
|
ret = airspy_set_lna_gain( _dev, value );
|
||||||
|
if ( AIRSPY_SUCCESS == ret ) {
|
||||||
|
_lna_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
// AIRSPY_THROW_ON_ERROR( ret, AIRSPY_FUNC_STR( "airspy_set_lna_gain", value ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _lna_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_gain( double gain, const std::string & name, size_t chan)
|
||||||
|
{
|
||||||
|
if ( "LNA" == name ) {
|
||||||
|
return set_gain( gain, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "MIX" == name ) {
|
||||||
|
return set_mix_gain( gain, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "IF" == name ) {
|
||||||
|
return set_if_gain( gain, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
return set_gain( gain, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_gain( size_t chan )
|
||||||
|
{
|
||||||
|
return _lna_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_gain( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name ) {
|
||||||
|
return get_gain( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "MIX" == name ) {
|
||||||
|
return _mix_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( "IF" == name ) {
|
||||||
|
return _vga_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_gain( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_mix_gain(double gain, size_t chan)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
osmosdr::gain_range_t gains = get_gain_range( "MIX", chan );
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
double clip_gain = gains.clip( gain, true );
|
||||||
|
uint8_t value = clip_gain;
|
||||||
|
|
||||||
|
ret = airspy_set_mixer_gain( _dev, value );
|
||||||
|
if ( AIRSPY_SUCCESS == ret ) {
|
||||||
|
_mix_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
// AIRSPY_THROW_ON_ERROR( ret, AIRSPY_FUNC_STR( "airspy_set_mixer_gain", value ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _mix_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_if_gain(double gain, size_t chan)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
osmosdr::gain_range_t gains = get_gain_range( "MIX", chan );
|
||||||
|
|
||||||
|
if (_dev) {
|
||||||
|
double clip_gain = gains.clip( gain, true );
|
||||||
|
uint8_t value = clip_gain;
|
||||||
|
|
||||||
|
ret = airspy_set_vga_gain( _dev, value );
|
||||||
|
if ( AIRSPY_SUCCESS == ret ) {
|
||||||
|
_vga_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
// AIRSPY_THROW_ON_ERROR( ret, AIRSPY_FUNC_STR( "airspy_set_vga_gain", value ) )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return _vga_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::string > airspy_source_c::get_antennas( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > antennas;
|
||||||
|
|
||||||
|
antennas += get_antenna( chan );
|
||||||
|
|
||||||
|
return antennas;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string airspy_source_c::set_antenna( const std::string & antenna, size_t chan )
|
||||||
|
{
|
||||||
|
return get_antenna( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string airspy_source_c::get_antenna( size_t chan )
|
||||||
|
{
|
||||||
|
return "RX";
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
{
|
||||||
|
return get_bandwidth( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double airspy_source_c::get_bandwidth( size_t chan )
|
||||||
|
{
|
||||||
|
return 10e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t airspy_source_c::get_bandwidth_range( size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::freq_range_t bandwidths;
|
||||||
|
|
||||||
|
bandwidths += osmosdr::range_t( get_bandwidth( chan ) );
|
||||||
|
|
||||||
|
return bandwidths;
|
||||||
|
}
|
|
@ -0,0 +1,146 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* This file is part of GNU Radio
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_AIRSPY_SOURCE_C_H
|
||||||
|
#define INCLUDED_AIRSPY_SOURCE_C_H
|
||||||
|
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
|
||||||
|
#include <gnuradio/gr_sync_block.h>
|
||||||
|
|
||||||
|
#include <libairspy/airspy.h>
|
||||||
|
|
||||||
|
#include "osmosdr_src_iface.h"
|
||||||
|
|
||||||
|
class airspy_source_c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||||
|
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||||
|
* us transparent reference counting, which greatly simplifies storage
|
||||||
|
* management issues. This is especially helpful in our hybrid
|
||||||
|
* C++ / Python system.
|
||||||
|
*
|
||||||
|
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||||
|
*
|
||||||
|
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||||
|
*/
|
||||||
|
typedef boost::shared_ptr<airspy_source_c> airspy_source_c_sptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return a shared_ptr to a new instance of airspy_source_c.
|
||||||
|
*
|
||||||
|
* To avoid accidental use of raw pointers, airspy_source_c's
|
||||||
|
* constructor is private. make_airspy_source_c is the public
|
||||||
|
* interface for creating new instances.
|
||||||
|
*/
|
||||||
|
airspy_source_c_sptr make_airspy_source_c (const std::string & args = "");
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Provides a stream of complex samples.
|
||||||
|
* \ingroup block
|
||||||
|
*/
|
||||||
|
class airspy_source_c :
|
||||||
|
public gr_sync_block,
|
||||||
|
public osmosdr_src_iface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The friend declaration allows make_airspy_source_c to
|
||||||
|
// access the private constructor.
|
||||||
|
|
||||||
|
friend airspy_source_c_sptr make_airspy_source_c (const std::string & args);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Provides a stream of complex samples.
|
||||||
|
*/
|
||||||
|
airspy_source_c (const std::string & args); // private constructor
|
||||||
|
|
||||||
|
public:
|
||||||
|
~airspy_source_c (); // public destructor
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
int work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items );
|
||||||
|
|
||||||
|
static std::vector< std::string > get_devices();
|
||||||
|
|
||||||
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
|
double set_sample_rate( double rate );
|
||||||
|
double get_sample_rate( void );
|
||||||
|
|
||||||
|
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
|
||||||
|
double set_center_freq( double freq, size_t chan = 0 );
|
||||||
|
double get_center_freq( size_t chan = 0 );
|
||||||
|
double set_freq_corr( double ppm, size_t chan = 0 );
|
||||||
|
double get_freq_corr( size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector<std::string> get_gain_names( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
|
||||||
|
bool set_gain_mode( bool automatic, size_t chan = 0 );
|
||||||
|
bool get_gain_mode( size_t chan = 0 );
|
||||||
|
double set_gain( double gain, size_t chan = 0 );
|
||||||
|
double set_gain( double gain, const std::string & name, size_t chan = 0 );
|
||||||
|
double get_gain( size_t chan = 0 );
|
||||||
|
double get_gain( const std::string & name, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_mix_gain(double gain, size_t chan = 0 );
|
||||||
|
double set_if_gain( double gain, size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector< std::string > get_antennas( size_t chan = 0 );
|
||||||
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int _airspy_rx_callback(airspy_transfer* transfer);
|
||||||
|
int airspy_rx_callback(void *samples, int sample_count);
|
||||||
|
|
||||||
|
static int _usage;
|
||||||
|
static boost::mutex _usage_mutex;
|
||||||
|
|
||||||
|
airspy_device *_dev;
|
||||||
|
|
||||||
|
boost::circular_buffer<gr_complex> *_fifo;
|
||||||
|
boost::mutex _fifo_lock;
|
||||||
|
boost::condition_variable _samp_avail;
|
||||||
|
|
||||||
|
double _sample_rate;
|
||||||
|
double _center_freq;
|
||||||
|
double _freq_corr;
|
||||||
|
bool _auto_gain;
|
||||||
|
double _lna_gain;
|
||||||
|
double _mix_gain;
|
||||||
|
double _vga_gain;
|
||||||
|
double _bandwidth;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INCLUDED_AIRSPY_SOURCE_C_H */
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Copyright 2013 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This file is part of GNU Radio
|
||||||
|
#
|
||||||
|
# GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# This file included, use CMake directory variables
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${LIBBLADERF_INCLUDE_DIRS}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(bladerf_srcs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_source_c.cc
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_sink_c.cc
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/bladerf_common.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Append gnuradio-osmosdr library sources
|
||||||
|
########################################################################
|
||||||
|
list(APPEND gr_osmosdr_srcs ${bladerf_srcs})
|
||||||
|
list(APPEND gr_osmosdr_libs ${LIBBLADERF_LIBRARIES})
|
|
@ -0,0 +1,510 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config.h is generated by configure. It contains the results
|
||||||
|
* of probing for features, options etc. It should be the first
|
||||||
|
* file included in your .cc file.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/shared_ptr.hpp>
|
||||||
|
|
||||||
|
#include "bladerf_common.h"
|
||||||
|
|
||||||
|
#define NUM_BUFFERS 32
|
||||||
|
#define NUM_SAMPLES_PER_BUFFER (4 * 1024)
|
||||||
|
|
||||||
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
boost::mutex bladerf_common::_devs_mutex;
|
||||||
|
std::list<boost::weak_ptr<struct bladerf> > bladerf_common::_devs;
|
||||||
|
|
||||||
|
bladerf_common::bladerf_common() : _conv_buf(NULL), _conv_buf_size(4096) {}
|
||||||
|
|
||||||
|
bladerf_common::~bladerf_common()
|
||||||
|
{
|
||||||
|
free(_conv_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_sptr bladerf_common:: get_cached_device(struct bladerf_devinfo devinfo)
|
||||||
|
{
|
||||||
|
/* Lock to _devs must be aquired by caller */
|
||||||
|
BOOST_FOREACH( boost::weak_ptr<struct bladerf> dev, _devs )
|
||||||
|
{
|
||||||
|
struct bladerf_devinfo other_devinfo;
|
||||||
|
|
||||||
|
int rv = bladerf_get_devinfo(bladerf_sptr(dev).get(), &other_devinfo);
|
||||||
|
if (rv < 0)
|
||||||
|
throw std::runtime_error(std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to get devinfo for cached device.");
|
||||||
|
|
||||||
|
if (bladerf_devinfo_matches(&devinfo, &other_devinfo)) {
|
||||||
|
return bladerf_sptr(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bladerf_sptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is called when a bladerf_sptr hits a refcount of 0 */
|
||||||
|
void bladerf_common::close(void* dev)
|
||||||
|
{
|
||||||
|
boost::unique_lock<boost::mutex> lock(_devs_mutex);
|
||||||
|
|
||||||
|
/* Prune expired entries from device cache */
|
||||||
|
std::list<boost::weak_ptr<struct bladerf> >::iterator it(_devs.begin());
|
||||||
|
while ( it != _devs.end() ) {
|
||||||
|
if ( (*it).expired() ) {
|
||||||
|
it = _devs.erase(it);
|
||||||
|
} else {
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_close((struct bladerf *)dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_sptr bladerf_common::open(const std::string &device_name)
|
||||||
|
{
|
||||||
|
int rv;
|
||||||
|
struct bladerf *raw_dev;
|
||||||
|
struct bladerf_devinfo devinfo;
|
||||||
|
|
||||||
|
boost::unique_lock<boost::mutex> lock(_devs_mutex);
|
||||||
|
|
||||||
|
rv = bladerf_get_devinfo_from_str(device_name.c_str(), &devinfo);
|
||||||
|
if (rv < 0)
|
||||||
|
throw std::runtime_error(std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to get devinfo for '" + device_name + "'");
|
||||||
|
|
||||||
|
bladerf_sptr cached_dev = get_cached_device(devinfo);
|
||||||
|
|
||||||
|
if (cached_dev)
|
||||||
|
return cached_dev;
|
||||||
|
|
||||||
|
rv = bladerf_open_with_devinfo(&raw_dev, &devinfo);
|
||||||
|
if (rv < 0)
|
||||||
|
throw std::runtime_error(std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to open device for '" + device_name + "'");
|
||||||
|
|
||||||
|
bladerf_sptr dev = bladerf_sptr(raw_dev, bladerf_common::close);
|
||||||
|
|
||||||
|
_devs.push_back(boost::weak_ptr<struct bladerf>(dev));
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_common::set_loopback_mode(const std::string &loopback)
|
||||||
|
{
|
||||||
|
bladerf_loopback mode;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (loopback == "bb_txlpf_rxvga2") {
|
||||||
|
mode = BLADERF_LB_BB_TXLPF_RXVGA2;
|
||||||
|
} else if (loopback == "bb_txlpf_rxlpf") {
|
||||||
|
mode = BLADERF_LB_BB_TXLPF_RXLPF;
|
||||||
|
} else if (loopback == "bb_txvga1_rxvga2") {
|
||||||
|
mode = BLADERF_LB_BB_TXVGA1_RXVGA2;
|
||||||
|
} else if (loopback == "bb_txvga1_rxlpf") {
|
||||||
|
mode = BLADERF_LB_BB_TXVGA1_RXLPF;
|
||||||
|
} else if (loopback == "rf_lna1") {
|
||||||
|
mode = BLADERF_LB_RF_LNA1;
|
||||||
|
} else if (loopback == "rf_lna2") {
|
||||||
|
mode = BLADERF_LB_RF_LNA2;
|
||||||
|
} else if (loopback == "rf_lna3") {
|
||||||
|
mode = BLADERF_LB_RF_LNA3;
|
||||||
|
} else if (loopback == "none") {
|
||||||
|
mode = BLADERF_LB_NONE;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( _pfx + "Invalid loopback mode:" + loopback );
|
||||||
|
}
|
||||||
|
|
||||||
|
status = bladerf_set_loopback( _dev.get(), mode);
|
||||||
|
if ( status != 0 ) {
|
||||||
|
throw std::runtime_error( _pfx + "Failed to set loopback mode: " +
|
||||||
|
bladerf_strerror(status) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_common::set_verbosity(const std::string &verbosity)
|
||||||
|
{
|
||||||
|
bladerf_log_level l;
|
||||||
|
|
||||||
|
if (verbosity == "verbose") {
|
||||||
|
l = BLADERF_LOG_LEVEL_VERBOSE;
|
||||||
|
} else if (verbosity == "debug") {
|
||||||
|
l = BLADERF_LOG_LEVEL_DEBUG;
|
||||||
|
} else if (verbosity == "info") {
|
||||||
|
l = BLADERF_LOG_LEVEL_INFO;
|
||||||
|
} else if (verbosity == "warning") {
|
||||||
|
l = BLADERF_LOG_LEVEL_WARNING;
|
||||||
|
} else if (verbosity == "error") {
|
||||||
|
l = BLADERF_LOG_LEVEL_ERROR;
|
||||||
|
} else if (verbosity == "critical") {
|
||||||
|
l = BLADERF_LOG_LEVEL_CRITICAL;
|
||||||
|
} else if (verbosity == "silent") {
|
||||||
|
l = BLADERF_LOG_LEVEL_SILENT;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( _pfx + "Invalid log level: " + verbosity );
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_log_set_verbosity(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_common::start(bladerf_module module)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_sync_config(_dev.get(), module, BLADERF_FORMAT_SC16_Q11,
|
||||||
|
_num_buffers, _samples_per_buffer,
|
||||||
|
_num_transfers, _stream_timeout_ms);
|
||||||
|
|
||||||
|
if ( ret != 0 ) {
|
||||||
|
std::cerr << _pfx << "bladerf_sync_config failed: "
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = bladerf_enable_module(_dev.get(), module, true);
|
||||||
|
if ( ret != 0 ) {
|
||||||
|
std::cerr << _pfx << "bladerf_enable_module failed: "
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_common::stop(bladerf_module module)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_enable_module(_dev.get(), module, false);
|
||||||
|
|
||||||
|
if ( ret != 0 ) {
|
||||||
|
std::cerr << _pfx << "bladerf_enable_modue failed: "
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_common::init(dict_t &dict, bladerf_module module)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
unsigned int device_number = 0;
|
||||||
|
std::string device_name;
|
||||||
|
struct bladerf_version ver;
|
||||||
|
char serial[BLADERF_SERIAL_LENGTH];
|
||||||
|
const char *type = (module == BLADERF_MODULE_TX ? "sink" : "source");
|
||||||
|
|
||||||
|
_pfx = std::string("[bladeRF ") + std::string(type) + std::string("] ");
|
||||||
|
|
||||||
|
if (dict.count("bladerf"))
|
||||||
|
{
|
||||||
|
std::string value = dict["bladerf"];
|
||||||
|
if ( value.length() )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
device_number = boost::lexical_cast< unsigned int >( value );
|
||||||
|
} catch ( std::exception &ex ) {
|
||||||
|
throw std::runtime_error( _pfx + "Failed to use '" + value +
|
||||||
|
"' as device number: " + ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device_name = boost::str(boost::format( "libusb:instance=%d" ) % device_number);
|
||||||
|
|
||||||
|
try {
|
||||||
|
_dev = open(device_name);
|
||||||
|
} catch(...) {
|
||||||
|
throw std::runtime_error( _pfx + "Failed to open bladeRF device " +
|
||||||
|
device_name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Load an FPGA */
|
||||||
|
if ( dict.count("fpga") )
|
||||||
|
{
|
||||||
|
|
||||||
|
if ( dict.count("fpga-reload") == 0 &&
|
||||||
|
bladerf_is_fpga_configured( _dev.get() ) == 1 ) {
|
||||||
|
|
||||||
|
std::cerr << _pfx << "FPGA is already loaded. Set fpga-reload=1 "
|
||||||
|
<< "to force a reload." << std::endl;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
std::string fpga = dict["fpga"];
|
||||||
|
|
||||||
|
std::cerr << _pfx << "Loading FPGA bitstream " << fpga << "..." << std::endl;
|
||||||
|
ret = bladerf_load_fpga( _dev.get(), fpga.c_str() );
|
||||||
|
if ( ret != 0 )
|
||||||
|
std::cerr << _pfx << "bladerf_load_fpga has failed with " << ret << std::endl;
|
||||||
|
else
|
||||||
|
std::cerr << _pfx << "The FPGA bitstream has been successfully loaded." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bladerf_is_fpga_configured( _dev.get() ) != 1 )
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << _pfx << "The FPGA is not configured! "
|
||||||
|
<< "Provide device argument fpga=/path/to/the/bitstream.rbf to load it.";
|
||||||
|
|
||||||
|
throw std::runtime_error( oss.str() );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( dict.count("loopback") )
|
||||||
|
set_loopback_mode( dict["loopback"] );
|
||||||
|
else
|
||||||
|
set_loopback_mode( "none" );
|
||||||
|
|
||||||
|
if ( dict.count("verbosity") )
|
||||||
|
set_verbosity( dict["verbosity"] );
|
||||||
|
|
||||||
|
|
||||||
|
/* Show some info about the device we've opened */
|
||||||
|
std::cerr << _pfx << "Using nuand LLC bladeRF #" << device_number;
|
||||||
|
|
||||||
|
if ( bladerf_get_serial( _dev.get(), serial ) == 0 )
|
||||||
|
{
|
||||||
|
std::string strser(serial);
|
||||||
|
|
||||||
|
if ( strser.length() == 32 )
|
||||||
|
strser.replace( 4, 24, "..." );
|
||||||
|
|
||||||
|
std::cerr << " SN " << strser;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( bladerf_fw_version( _dev.get(), &ver ) == 0 )
|
||||||
|
std::cerr << " FW v" << ver.major << "." << ver.minor << "." << ver.patch;
|
||||||
|
|
||||||
|
if ( bladerf_fpga_version( _dev.get(), &ver ) == 0 )
|
||||||
|
std::cerr << " FPGA v" << ver.major << "." << ver.minor << "." << ver.patch;
|
||||||
|
|
||||||
|
std::cerr << std::endl;
|
||||||
|
|
||||||
|
/* Initialize buffer and sample configuration */
|
||||||
|
_num_buffers = 0;
|
||||||
|
if (dict.count("buffers")) {
|
||||||
|
_num_buffers = boost::lexical_cast< size_t >( dict["buffers"] );
|
||||||
|
}
|
||||||
|
|
||||||
|
_samples_per_buffer = 0;
|
||||||
|
if (dict.count("buflen")) {
|
||||||
|
_samples_per_buffer = boost::lexical_cast< size_t >( dict["buflen"] );
|
||||||
|
}
|
||||||
|
|
||||||
|
_num_transfers = 0;
|
||||||
|
if (dict.count("transfers")) {
|
||||||
|
_num_transfers = boost::lexical_cast< size_t >( dict["transfers"] );
|
||||||
|
}
|
||||||
|
|
||||||
|
_stream_timeout_ms = 3000;
|
||||||
|
if (dict.count("stream_timeout_ms")) {
|
||||||
|
_stream_timeout_ms = boost::lexical_cast< unsigned int >(dict["stream_timeout_ms"] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Require value to be >= 2 so we can ensure we have twice as many
|
||||||
|
* buffers as transfers */
|
||||||
|
if (_num_buffers <= 1) {
|
||||||
|
_num_buffers = NUM_BUFFERS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 == _samples_per_buffer) {
|
||||||
|
_samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
|
||||||
|
} else {
|
||||||
|
if (_samples_per_buffer < 1024 || _samples_per_buffer % 1024 != 0) {
|
||||||
|
|
||||||
|
/* 0 likely implies the user did not specify this, so don't warn */
|
||||||
|
if (_samples_per_buffer != 0 ) {
|
||||||
|
std::cerr << _pfx << "Invalid \"buflen\" value. "
|
||||||
|
<< "A multiple of 1024 is required. Defaulting to "
|
||||||
|
<< NUM_SAMPLES_PER_BUFFER << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_samples_per_buffer = NUM_SAMPLES_PER_BUFFER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (_num_transfers == 0 || _num_transfers > (_num_buffers / 2)) {
|
||||||
|
_num_transfers = _num_buffers / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
_conv_buf = static_cast<int16_t*>(malloc(_conv_buf_size * 2 * sizeof(int16_t)));
|
||||||
|
|
||||||
|
if (_conv_buf == NULL) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) +
|
||||||
|
"Failed to allocate _conv_buf" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_common::freq_range()
|
||||||
|
{
|
||||||
|
/* assuming the same for RX & TX */
|
||||||
|
return osmosdr::freq_range_t( 300e6, 3.8e9 );
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::meta_range_t bladerf_common::sample_rates()
|
||||||
|
{
|
||||||
|
osmosdr::meta_range_t sample_rates;
|
||||||
|
|
||||||
|
/* assuming the same for RX & TX */
|
||||||
|
sample_rates += osmosdr::range_t( 160e3, 200e3, 40e3 );
|
||||||
|
sample_rates += osmosdr::range_t( 300e3, 900e3, 100e3 );
|
||||||
|
sample_rates += osmosdr::range_t( 1e6, 40e6, 1e6 );
|
||||||
|
|
||||||
|
return sample_rates;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_common::filter_bandwidths()
|
||||||
|
{
|
||||||
|
/* the same for RX & TX according to the datasheet */
|
||||||
|
osmosdr::freq_range_t bandwidths;
|
||||||
|
|
||||||
|
std::vector<double> half_bandwidths; /* in MHz */
|
||||||
|
half_bandwidths += \
|
||||||
|
0.75, 0.875, 1.25, 1.375, 1.5, 1.92, 2.5,
|
||||||
|
2.75, 3, 3.5, 4.375, 5, 6, 7, 10, 14;
|
||||||
|
|
||||||
|
BOOST_FOREACH( double half_bw, half_bandwidths )
|
||||||
|
bandwidths += osmosdr::range_t( half_bw * 2e6 );
|
||||||
|
|
||||||
|
return bandwidths;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::string > bladerf_common::devices()
|
||||||
|
{
|
||||||
|
struct bladerf_devinfo *devices;
|
||||||
|
ssize_t n_devices;
|
||||||
|
std::vector< std::string > ret;
|
||||||
|
|
||||||
|
n_devices = bladerf_get_device_list(&devices);
|
||||||
|
|
||||||
|
if (n_devices > 0)
|
||||||
|
{
|
||||||
|
for (ssize_t i = 0; i < n_devices; i++)
|
||||||
|
{
|
||||||
|
std::stringstream s;
|
||||||
|
std::string serial(devices[i].serial);
|
||||||
|
|
||||||
|
s << "bladerf=" << devices[i].instance << ","
|
||||||
|
<< "label='nuand bladeRF";
|
||||||
|
|
||||||
|
if ( serial.length() == 32 )
|
||||||
|
serial.replace( 4, 24, "..." );
|
||||||
|
|
||||||
|
if ( serial.length() )
|
||||||
|
s << " SN " << serial;
|
||||||
|
|
||||||
|
s << "'";
|
||||||
|
|
||||||
|
ret.push_back(s.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bladerf_free_device_list(devices);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_common::set_sample_rate( bladerf_module module, double rate )
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
struct bladerf_rational_rate rational_rate, actual;
|
||||||
|
|
||||||
|
rational_rate.integer = (uint32_t)rate;
|
||||||
|
rational_rate.den = 10000;
|
||||||
|
rational_rate.num = (rate - rational_rate.integer) * rational_rate.den;
|
||||||
|
|
||||||
|
status = bladerf_set_rational_sample_rate( _dev.get(), module,
|
||||||
|
&rational_rate, &actual );
|
||||||
|
|
||||||
|
if ( status != 0 ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to set integer rate:" +
|
||||||
|
std::string(bladerf_strerror(status)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return actual.integer + actual.num / (double)actual.den;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_common::get_sample_rate( bladerf_module module )
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
double ret = 0.0;
|
||||||
|
struct bladerf_rational_rate rate;
|
||||||
|
|
||||||
|
|
||||||
|
status = bladerf_get_rational_sample_rate( _dev.get(), module, &rate );
|
||||||
|
|
||||||
|
if ( status != 0 ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) +
|
||||||
|
"Failed to get sample rate:" +
|
||||||
|
std::string(bladerf_strerror(status)) );
|
||||||
|
} else {
|
||||||
|
ret = rate.integer + rate.num / (double)rate.den;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bladerf_common::set_dc_offset(bladerf_module module, const std::complex<double> &offset, size_t chan)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int16_t val_i, val_q;
|
||||||
|
|
||||||
|
val_i = (int16_t)(offset.real() * DCOFF_SCALE);
|
||||||
|
val_q = (int16_t)(offset.imag() * DCOFF_SCALE);
|
||||||
|
|
||||||
|
ret = bladerf_set_correction(_dev.get(), module, BLADERF_CORR_LMS_DCOFF_I, val_i);
|
||||||
|
ret |= bladerf_set_correction(_dev.get(), module, BLADERF_CORR_LMS_DCOFF_Q, val_q);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bladerf_common::set_iq_balance(bladerf_module module, const std::complex<double> &balance, size_t chan)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
int16_t val_gain, val_phase;
|
||||||
|
|
||||||
|
val_gain = (int16_t)(balance.real() * GAIN_SCALE);
|
||||||
|
val_phase = (int16_t)(balance.imag() * PHASE_SCALE);
|
||||||
|
|
||||||
|
ret = bladerf_set_correction(_dev.get(), module, BLADERF_CORR_FPGA_GAIN, val_gain);
|
||||||
|
ret |= bladerf_set_correction(_dev.get(), module, BLADERF_CORR_FPGA_PHASE, val_phase);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,102 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_BLADERF_COMMON_H
|
||||||
|
#define INCLUDED_BLADERF_COMMON_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/shared_mutex.hpp>
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
#include <gr_complex.h>
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
|
||||||
|
#include <libbladeRF.h>
|
||||||
|
|
||||||
|
#include "osmosdr/osmosdr_ranges.h"
|
||||||
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
|
||||||
|
typedef boost::shared_ptr<struct bladerf> bladerf_sptr;
|
||||||
|
|
||||||
|
class bladerf_common
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bladerf_common();
|
||||||
|
virtual ~bladerf_common();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/* Handle initialized and parameters common to both source & sink */
|
||||||
|
void init(dict_t &dict, bladerf_module module);
|
||||||
|
|
||||||
|
bool start(bladerf_module module);
|
||||||
|
bool stop(bladerf_module module);
|
||||||
|
|
||||||
|
double set_sample_rate(bladerf_module module, double rate);
|
||||||
|
double get_sample_rate(bladerf_module module);
|
||||||
|
|
||||||
|
int set_dc_offset(bladerf_module module, const std::complex<double> &offset, size_t chan);
|
||||||
|
int set_iq_balance(bladerf_module module, const std::complex<double> &balance, size_t chan);
|
||||||
|
|
||||||
|
osmosdr::freq_range_t freq_range();
|
||||||
|
osmosdr::meta_range_t sample_rates();
|
||||||
|
osmosdr::freq_range_t filter_bandwidths();
|
||||||
|
|
||||||
|
static std::vector< std::string > devices();
|
||||||
|
|
||||||
|
bladerf_sptr _dev;
|
||||||
|
|
||||||
|
size_t _num_buffers;
|
||||||
|
size_t _samples_per_buffer;
|
||||||
|
size_t _num_transfers;
|
||||||
|
unsigned int _stream_timeout_ms;
|
||||||
|
|
||||||
|
int16_t *_conv_buf;
|
||||||
|
int _conv_buf_size; /* In units of samples */
|
||||||
|
|
||||||
|
osmosdr::gain_range_t _vga1_range;
|
||||||
|
osmosdr::gain_range_t _vga2_range;
|
||||||
|
|
||||||
|
std::string _pfx;
|
||||||
|
|
||||||
|
/* BladeRF IQ correction parameters */
|
||||||
|
static const int16_t DCOFF_SCALE = 2048;
|
||||||
|
static const int16_t GAIN_SCALE = 4096;
|
||||||
|
static const int16_t PHASE_SCALE = 4096;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bladerf_sptr open(const std::string &device_name);
|
||||||
|
static void close(void *dev); /* called by shared_ptr */
|
||||||
|
static bladerf_sptr get_cached_device(struct bladerf_devinfo devinfo);
|
||||||
|
|
||||||
|
void set_verbosity(const std::string &verbosity);
|
||||||
|
void set_loopback_mode(const std::string &loopback);
|
||||||
|
|
||||||
|
static boost::mutex _devs_mutex;
|
||||||
|
static std::list<boost::weak_ptr<struct bladerf> > _devs;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,414 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config.h is generated by configure. It contains the results
|
||||||
|
* of probing for features, options etc. It should be the first
|
||||||
|
* file included in your .cc file.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
#include <gr_io_signature.h>
|
||||||
|
|
||||||
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
#include "bladerf_sink_c.h"
|
||||||
|
|
||||||
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new instance of bladerf_sink_c and return
|
||||||
|
* a boost shared_ptr. This is effectively the public constructor.
|
||||||
|
*/
|
||||||
|
bladerf_sink_c_sptr make_bladerf_sink_c (const std::string &args)
|
||||||
|
{
|
||||||
|
return gnuradio::get_initial_sptr(new bladerf_sink_c (args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify constraints on number of input and output streams.
|
||||||
|
* This info is used to construct the input and output signatures
|
||||||
|
* (2nd & 3rd args to gr_block's constructor). The input and
|
||||||
|
* output signatures are used by the runtime system to
|
||||||
|
* check that a valid number and type of inputs and outputs
|
||||||
|
* are connected to this block. In this case, we accept
|
||||||
|
* only 0 input and 1 output.
|
||||||
|
*/
|
||||||
|
static const int MIN_IN = 1; // mininum number of input streams
|
||||||
|
static const int MAX_IN = 1; // maximum number of input streams
|
||||||
|
static const int MIN_OUT = 0; // minimum number of output streams
|
||||||
|
static const int MAX_OUT = 0; // maximum number of output streams
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The private constructor
|
||||||
|
*/
|
||||||
|
bladerf_sink_c::bladerf_sink_c (const std::string &args)
|
||||||
|
: gr_sync_block ("bladerf_sink_c",
|
||||||
|
gr_make_io_signature (MIN_IN, MAX_IN, sizeof (gr_complex)),
|
||||||
|
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
|
||||||
|
{
|
||||||
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
|
/* Perform src/sink agnostic initializations */
|
||||||
|
init(dict, BLADERF_MODULE_TX);
|
||||||
|
|
||||||
|
/* Set the range of VGA1, VGA1GAINT[7:0] */
|
||||||
|
_vga1_range = osmosdr::gain_range_t( -35, -4, 1 );
|
||||||
|
|
||||||
|
/* Set the range of VGA2, VGA2GAIN[4:0] */
|
||||||
|
_vga2_range = osmosdr::gain_range_t( 0, 25, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_sink_c::start()
|
||||||
|
{
|
||||||
|
return bladerf_common::start(BLADERF_MODULE_TX);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_sink_c::stop()
|
||||||
|
{
|
||||||
|
return bladerf_common::stop(BLADERF_MODULE_TX);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bladerf_sink_c::work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items )
|
||||||
|
{
|
||||||
|
const gr_complex *in = (const gr_complex *) input_items[0];
|
||||||
|
struct bladerf_metadata meta;
|
||||||
|
const float scaling = 2000.0f;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (noutput_items > _conv_buf_size) {
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
_conv_buf_size = noutput_items;
|
||||||
|
tmp = realloc(_conv_buf, _conv_buf_size * 2 * sizeof(int16_t));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) +
|
||||||
|
"Failed to realloc _conv_buf" );
|
||||||
|
}
|
||||||
|
|
||||||
|
_conv_buf = static_cast<int16_t*>(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert floating point samples into fixed point */
|
||||||
|
for (int i = 0; i < 2 * noutput_items;) {
|
||||||
|
_conv_buf[i++] = (int16_t)(scaling * real(*in));
|
||||||
|
_conv_buf[i++] = (int16_t)(scaling * imag(*in++));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Submit them to the device */
|
||||||
|
ret = bladerf_sync_tx(_dev.get(), static_cast<void *>(_conv_buf),
|
||||||
|
noutput_items, &meta, _stream_timeout_ms);
|
||||||
|
|
||||||
|
if ( ret != 0 ) {
|
||||||
|
std::cerr << _pfx << "bladerf_sync_tx error: "
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
return WORK_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return noutput_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::string> bladerf_sink_c::get_devices()
|
||||||
|
{
|
||||||
|
return bladerf_common::devices();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bladerf_sink_c::get_num_channels()
|
||||||
|
{
|
||||||
|
/* We only support a single channel for each bladeRF */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::meta_range_t bladerf_sink_c::get_sample_rates()
|
||||||
|
{
|
||||||
|
return sample_rates();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_sample_rate(double rate)
|
||||||
|
{
|
||||||
|
return bladerf_common::set_sample_rate(BLADERF_MODULE_TX, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_sample_rate()
|
||||||
|
{
|
||||||
|
return bladerf_common::get_sample_rate(BLADERF_MODULE_TX);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_sink_c::get_freq_range( size_t chan )
|
||||||
|
{
|
||||||
|
return freq_range();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_center_freq( double freq, size_t chan )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check frequency range */
|
||||||
|
if( freq < get_freq_range( chan ).start() ||
|
||||||
|
freq > get_freq_range( chan ).stop() ) {
|
||||||
|
std::cerr << "Failed to set out of bound frequency: " << freq << std::endl;
|
||||||
|
} else {
|
||||||
|
ret = bladerf_set_frequency( _dev.get(), BLADERF_MODULE_TX, (uint32_t)freq );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to set center frequency " +
|
||||||
|
boost::lexical_cast<std::string>(freq) +
|
||||||
|
":" + std::string(bladerf_strerror(ret)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_center_freq( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_center_freq( size_t chan )
|
||||||
|
{
|
||||||
|
uint32_t freq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_get_frequency( _dev.get(), BLADERF_MODULE_TX, &freq );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Failed to get center frequency:" +
|
||||||
|
std::string(bladerf_strerror(ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_freq_corr( double ppm, size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Write the VCTCXO with a correction value (also changes RX ppm value!) */
|
||||||
|
return get_freq_corr( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_freq_corr( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Return back the frequency correction in ppm */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> bladerf_sink_c::get_gain_names( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > names;
|
||||||
|
|
||||||
|
names += "VGA1", "VGA2";
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t bladerf_sink_c::get_gain_range( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: This is an overall system gain range. Given the VGA1 and VGA2
|
||||||
|
how much total gain can we have in the system */
|
||||||
|
return get_gain_range( "VGA2", chan ); /* we use only VGA2 here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t bladerf_sink_c::get_gain_range( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::gain_range_t range;
|
||||||
|
|
||||||
|
if( name == "VGA1" ) {
|
||||||
|
range = _vga1_range;
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
range = _vga2_range;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Requested an invalid gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_sink_c::set_gain_mode( bool automatic, size_t chan )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_sink_c::get_gain_mode( size_t chan )
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_gain( double gain, size_t chan )
|
||||||
|
{
|
||||||
|
return set_gain( gain, "VGA2", chan ); /* we use only VGA2 here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_gain( double gain, const std::string & name, size_t chan)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if( name == "VGA1" ) {
|
||||||
|
ret = bladerf_set_txvga1( _dev.get(), (int)gain );
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
ret = bladerf_set_txvga2( _dev.get(), (int)gain );
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Requested to set the gain " +
|
||||||
|
"of an unknown gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error(std::string(__FUNCTION__) + " " +
|
||||||
|
"Could not set " + name + " gain, error " +
|
||||||
|
std::string(bladerf_strerror(ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_gain( name, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_gain( size_t chan )
|
||||||
|
{
|
||||||
|
return get_gain( "VGA2", chan ); /* we use only VGA2 here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_gain( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
int g;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if( name == "VGA1" ) {
|
||||||
|
ret = bladerf_get_txvga1( _dev.get(), &g );
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
ret = bladerf_get_txvga2( _dev.get(), &g );
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Requested to get the gain " +
|
||||||
|
"of an unknown gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"Could not get " + name + " gain, error " +
|
||||||
|
std::string(bladerf_strerror(ret)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)g;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_bb_gain( double gain, size_t chan )
|
||||||
|
{
|
||||||
|
/* for TX, only VGA1 is in the BB path */
|
||||||
|
osmosdr::gain_range_t bb_gains = get_gain_range( "VGA1", chan );
|
||||||
|
|
||||||
|
double clip_gain = bb_gains.clip( gain, true );
|
||||||
|
gain = set_gain( clip_gain, "VGA1", chan );
|
||||||
|
|
||||||
|
return gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::string > bladerf_sink_c::get_antennas( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > antennas;
|
||||||
|
|
||||||
|
antennas += get_antenna( chan );
|
||||||
|
|
||||||
|
return antennas;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bladerf_sink_c::set_antenna( const std::string & antenna, size_t chan )
|
||||||
|
{
|
||||||
|
return get_antenna( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bladerf_sink_c::get_antenna( size_t chan )
|
||||||
|
{
|
||||||
|
/* We only have a single transmit antenna here */
|
||||||
|
return "TX";
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_sink_c::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = bladerf_common::set_dc_offset(BLADERF_MODULE_TX, offset, chan);
|
||||||
|
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set dc offset: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_sink_c::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = bladerf_common::set_iq_balance(BLADERF_MODULE_TX, balance, chan);
|
||||||
|
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set iq balance: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint32_t actual;
|
||||||
|
|
||||||
|
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
||||||
|
bandwidth = get_sample_rate() * 0.75; /* select narrower filters to prevent aliasing */
|
||||||
|
|
||||||
|
ret = bladerf_set_bandwidth( _dev.get(), BLADERF_MODULE_TX, (uint32_t)bandwidth, &actual );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set bandwidth:" +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_bandwidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_sink_c::get_bandwidth( size_t chan )
|
||||||
|
{
|
||||||
|
uint32_t bandwidth;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_get_bandwidth( _dev.get(), BLADERF_MODULE_TX, &bandwidth );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not get bandwidth: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)bandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_sink_c::get_bandwidth_range( size_t chan )
|
||||||
|
{
|
||||||
|
return filter_bandwidths();
|
||||||
|
}
|
|
@ -0,0 +1,115 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_BLADERF_SINK_C_H
|
||||||
|
#define INCLUDED_BLADERF_SINK_C_H
|
||||||
|
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
#include <gr_block.h>
|
||||||
|
#include <gr_sync_block.h>
|
||||||
|
|
||||||
|
#include "osmosdr/osmosdr_ranges.h"
|
||||||
|
#include "osmosdr_snk_iface.h"
|
||||||
|
|
||||||
|
#include "bladerf_common.h"
|
||||||
|
|
||||||
|
class bladerf_sink_c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||||
|
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||||
|
* us transparent reference counting, which greatly simplifies storage
|
||||||
|
* management issues. This is especially helpful in our hybrid
|
||||||
|
* C++ / Python system.
|
||||||
|
*
|
||||||
|
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||||
|
*
|
||||||
|
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||||
|
*/
|
||||||
|
typedef boost::shared_ptr<bladerf_sink_c> bladerf_sink_c_sptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return a shared_ptr to a new instance of bladerf_sink_c.
|
||||||
|
*
|
||||||
|
* To avoid accidental use of raw pointers, bladerf_sink_c's
|
||||||
|
* constructor is private. make_bladerf_sink_c is the public
|
||||||
|
* interface for creating new instances.
|
||||||
|
*/
|
||||||
|
bladerf_sink_c_sptr make_bladerf_sink_c (const std::string & args = "");
|
||||||
|
|
||||||
|
class bladerf_sink_c :
|
||||||
|
public gr_sync_block,
|
||||||
|
public osmosdr_snk_iface,
|
||||||
|
protected bladerf_common
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The friend declaration allows bladerf_make_sink_c to
|
||||||
|
// access the private constructor.
|
||||||
|
friend bladerf_sink_c_sptr make_bladerf_sink_c (const std::string & args);
|
||||||
|
|
||||||
|
bladerf_sink_c (const std::string & args); // private constructor
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
int work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items );
|
||||||
|
|
||||||
|
static std::vector< std::string > get_devices();
|
||||||
|
|
||||||
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
|
double set_sample_rate( double rate );
|
||||||
|
double get_sample_rate( void );
|
||||||
|
|
||||||
|
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
|
||||||
|
double set_center_freq( double freq, size_t chan = 0 );
|
||||||
|
double get_center_freq( size_t chan = 0 );
|
||||||
|
double set_freq_corr( double ppm, size_t chan = 0 );
|
||||||
|
double get_freq_corr( size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector<std::string> get_gain_names( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
|
||||||
|
bool set_gain_mode( bool automatic, size_t chan = 0 );
|
||||||
|
bool get_gain_mode( size_t chan = 0 );
|
||||||
|
double set_gain( double gain, size_t chan = 0 );
|
||||||
|
double set_gain( double gain, const std::string & name, size_t chan = 0 );
|
||||||
|
double get_gain( size_t chan = 0 );
|
||||||
|
double get_gain( const std::string & name, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bb_gain( double gain, size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector< std::string > get_antennas( size_t chan = 0 );
|
||||||
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_dc_offset( const std::complex<double> &offset, size_t chan );
|
||||||
|
void set_iq_balance( const std::complex<double> &balance, size_t chan );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INCLUDED_BLADERF_SINK_C_H */
|
|
@ -0,0 +1,509 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* config.h is generated by configure. It contains the results
|
||||||
|
* of probing for features, options etc. It should be the first
|
||||||
|
* file included in your .cc file.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
#include <gr_io_signature.h>
|
||||||
|
|
||||||
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
#include "bladerf_source_c.h"
|
||||||
|
#include "osmosdr/osmosdr_source_c.h"
|
||||||
|
|
||||||
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a new instance of bladerf_source_c and return
|
||||||
|
* a boost shared_ptr. This is effectively the public constructor.
|
||||||
|
*/
|
||||||
|
bladerf_source_c_sptr make_bladerf_source_c (const std::string &args)
|
||||||
|
{
|
||||||
|
return gnuradio::get_initial_sptr(new bladerf_source_c (args));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Specify constraints on number of input and output streams.
|
||||||
|
* This info is used to construct the input and output signatures
|
||||||
|
* (2nd & 3rd args to gr_block's constructor). The input and
|
||||||
|
* output signatures are used by the runtime system to
|
||||||
|
* check that a valid number and type of inputs and outputs
|
||||||
|
* are connected to this block. In this case, we accept
|
||||||
|
* only 0 input and 1 output.
|
||||||
|
*/
|
||||||
|
static const int MIN_IN = 0; // mininum number of input streams
|
||||||
|
static const int MAX_IN = 0; // maximum number of input streams
|
||||||
|
static const int MIN_OUT = 1; // minimum number of output streams
|
||||||
|
static const int MAX_OUT = 1; // maximum number of output streams
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The private constructor
|
||||||
|
*/
|
||||||
|
bladerf_source_c::bladerf_source_c (const std::string &args)
|
||||||
|
: gr_sync_block ("bladerf_source_c",
|
||||||
|
gr_make_io_signature (MIN_IN, MAX_IN, sizeof (gr_complex)),
|
||||||
|
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
std::string device_name;
|
||||||
|
struct bladerf_version fpga_version;
|
||||||
|
|
||||||
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
|
init(dict, BLADERF_MODULE_RX);
|
||||||
|
|
||||||
|
if (dict.count("sampling"))
|
||||||
|
{
|
||||||
|
std::string sampling = dict["sampling"];
|
||||||
|
|
||||||
|
std::cerr << _pfx << "Setting bladerf sampling to " << sampling << std::endl;
|
||||||
|
if( sampling == "internal") {
|
||||||
|
ret = bladerf_set_sampling( _dev.get(), BLADERF_SAMPLING_INTERNAL );
|
||||||
|
if ( ret != 0 )
|
||||||
|
std::cerr << _pfx << "Problem while setting sampling mode:"
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
} else if( sampling == "external" ) {
|
||||||
|
ret = bladerf_set_sampling( _dev.get(), BLADERF_SAMPLING_EXTERNAL );
|
||||||
|
if ( ret != 0 )
|
||||||
|
std::cerr << _pfx << "Problem while setting sampling mode:"
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
} else {
|
||||||
|
std::cerr << _pfx << "Invalid sampling mode " << sampling << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the range of LNA, G_LNA_RXFE[1:0] */
|
||||||
|
_lna_range = osmosdr::gain_range_t( 0, 6, 3 );
|
||||||
|
|
||||||
|
/* Set the range of VGA1, RFB_TIA_RXFE[6:0], nonlinear mapping done inside the lib */
|
||||||
|
_vga1_range = osmosdr::gain_range_t( 5, 30, 1 );
|
||||||
|
|
||||||
|
/* Set the range of VGA2 VGA2GAIN[4:0], not recommended to be used above 30dB */
|
||||||
|
_vga2_range = osmosdr::gain_range_t( 0, 30, 3 );
|
||||||
|
|
||||||
|
/* Warn user about using an old FPGA version, as we no longer strip off the
|
||||||
|
* markers that were pressent in the pre-v0.0.1 FPGA */
|
||||||
|
if (bladerf_fpga_version( _dev.get(), &fpga_version ) != 0) {
|
||||||
|
std::cerr << _pfx << "Failed to get FPGA version" << std::endl;
|
||||||
|
} else if ( fpga_version.major <= 0 &&
|
||||||
|
fpga_version.minor <= 0 &&
|
||||||
|
fpga_version.patch < 1 ) {
|
||||||
|
|
||||||
|
std::cerr << _pfx << "Warning: FPGA version v0.0.1 or later is required. "
|
||||||
|
<< "Using an earlier FPGA version will result in misinterpeted samples. "
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_source_c::start()
|
||||||
|
{
|
||||||
|
return bladerf_common::start(BLADERF_MODULE_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_source_c::stop()
|
||||||
|
{
|
||||||
|
return bladerf_common::stop(BLADERF_MODULE_RX);
|
||||||
|
}
|
||||||
|
|
||||||
|
int bladerf_source_c::work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct bladerf_metadata meta;
|
||||||
|
int16_t *current;
|
||||||
|
const float scaling = 1.0f / 2048.0f;
|
||||||
|
gr_complex *out = static_cast<gr_complex *>(output_items[0]);
|
||||||
|
|
||||||
|
if (noutput_items > _conv_buf_size) {
|
||||||
|
void *tmp;
|
||||||
|
|
||||||
|
_conv_buf_size = noutput_items;
|
||||||
|
tmp = realloc(_conv_buf, _conv_buf_size * 2 * sizeof(int16_t));
|
||||||
|
if (tmp == NULL) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) +
|
||||||
|
"Failed to realloc _conv_buf" );
|
||||||
|
}
|
||||||
|
|
||||||
|
_conv_buf = static_cast<int16_t*>(tmp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grab all the samples into the temporary buffer */
|
||||||
|
ret = bladerf_sync_rx(_dev.get(), static_cast<void *>(_conv_buf),
|
||||||
|
noutput_items, &meta, _stream_timeout_ms);
|
||||||
|
if ( ret != 0 ) {
|
||||||
|
std::cerr << _pfx << "bladerf_sync_rx error: "
|
||||||
|
<< bladerf_strerror(ret) << std::endl;
|
||||||
|
return WORK_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = _conv_buf;
|
||||||
|
|
||||||
|
/* Convert them from fixed to floating point */
|
||||||
|
for (int i = 0; i < noutput_items; ++i) {
|
||||||
|
float x, y;
|
||||||
|
|
||||||
|
x = scaling * *current;
|
||||||
|
current++;
|
||||||
|
|
||||||
|
y = scaling * *current;
|
||||||
|
current++;
|
||||||
|
|
||||||
|
out[i] = gr_complex(x, y) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
return noutput_items;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> bladerf_source_c::get_devices()
|
||||||
|
{
|
||||||
|
return bladerf_common::devices();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t bladerf_source_c::get_num_channels()
|
||||||
|
{
|
||||||
|
/* We only support a single channel for each bladeRF */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::meta_range_t bladerf_source_c::get_sample_rates()
|
||||||
|
{
|
||||||
|
return sample_rates();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_sample_rate( double rate )
|
||||||
|
{
|
||||||
|
return bladerf_common::set_sample_rate( BLADERF_MODULE_RX, rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_sample_rate()
|
||||||
|
{
|
||||||
|
return bladerf_common::get_sample_rate( BLADERF_MODULE_RX );
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_source_c::get_freq_range( size_t chan )
|
||||||
|
{
|
||||||
|
return freq_range();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_center_freq( double freq, size_t chan )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Check frequency range */
|
||||||
|
if( freq < get_freq_range( chan ).start() ||
|
||||||
|
freq > get_freq_range( chan ).stop() ) {
|
||||||
|
std::cerr << "Failed to set out of bound frequency: " << freq << std::endl;
|
||||||
|
} else {
|
||||||
|
ret = bladerf_set_frequency( _dev.get(), BLADERF_MODULE_RX, (uint32_t)freq );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"failed to set center frequency " +
|
||||||
|
boost::lexical_cast<std::string>(freq) + ": " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_center_freq( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_center_freq( size_t chan )
|
||||||
|
{
|
||||||
|
uint32_t freq;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_get_frequency( _dev.get(), BLADERF_MODULE_RX, &freq );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"failed to get center frequency: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)freq;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_freq_corr( double ppm, size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Write the VCTCXO with a correction value (also changes TX ppm value!) */
|
||||||
|
return get_freq_corr( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_freq_corr( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Return back the frequency correction in ppm */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> bladerf_source_c::get_gain_names( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > names;
|
||||||
|
|
||||||
|
names += "LNA", "VGA1", "VGA2";
|
||||||
|
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t bladerf_source_c::get_gain_range( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: This is an overall system gain range. Given the LNA, VGA1 and VGA2
|
||||||
|
how much total gain can we have in the system */
|
||||||
|
return get_gain_range( "LNA", chan ); /* we use only LNA here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::gain_range_t bladerf_source_c::get_gain_range( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::gain_range_t range;
|
||||||
|
|
||||||
|
if( name == "LNA" ) {
|
||||||
|
range = _lna_range;
|
||||||
|
} else if( name == "VGA1" ) {
|
||||||
|
range = _vga1_range;
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
range = _vga2_range;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"requested an invalid gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_source_c::set_gain_mode( bool automatic, size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Implement AGC in the FPGA */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bladerf_source_c::get_gain_mode( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: Read back AGC mode */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_gain( double gain, size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: This is an overall system gain that has to be set */
|
||||||
|
return set_gain( gain, "LNA", chan ); /* we use only LNA here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_gain( double gain, const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if( name == "LNA" ) {
|
||||||
|
bladerf_lna_gain g;
|
||||||
|
|
||||||
|
if ( gain >= 6.0f )
|
||||||
|
g = BLADERF_LNA_GAIN_MAX;
|
||||||
|
else if ( gain >= 3.0f )
|
||||||
|
g = BLADERF_LNA_GAIN_MID;
|
||||||
|
else /* gain < 3.0f */
|
||||||
|
g = BLADERF_LNA_GAIN_BYPASS;
|
||||||
|
|
||||||
|
ret = bladerf_set_lna_gain( _dev.get(), g );
|
||||||
|
} else if( name == "VGA1" ) {
|
||||||
|
ret = bladerf_set_rxvga1( _dev.get(), (int)gain );
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
ret = bladerf_set_rxvga2( _dev.get(), (int)gain );
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"requested to set the gain "
|
||||||
|
"of an unknown gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set " + name + " gain: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_gain( name, chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_gain( size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: This is an overall system gain that has to be set */
|
||||||
|
return get_gain( "LNA", chan ); /* we use only LNA here for now */
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_gain( const std::string & name, size_t chan )
|
||||||
|
{
|
||||||
|
int g;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if( name == "LNA" ) {
|
||||||
|
bladerf_lna_gain lna_g;
|
||||||
|
ret = bladerf_get_lna_gain( _dev.get(), &lna_g );
|
||||||
|
g = lna_g == BLADERF_LNA_GAIN_BYPASS ? 0 : lna_g == BLADERF_LNA_GAIN_MID ? 3 : 6;
|
||||||
|
} else if( name == "VGA1" ) {
|
||||||
|
ret = bladerf_get_rxvga1( _dev.get(), &g );
|
||||||
|
} else if( name == "VGA2" ) {
|
||||||
|
ret = bladerf_get_rxvga2( _dev.get(), &g );
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"requested to get the gain "
|
||||||
|
"of an unknown gain element " + name );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for errors */
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not get " + name + " gain: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)g;
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_bb_gain( double gain, size_t chan )
|
||||||
|
{
|
||||||
|
/* TODO: for RX, we should combine VGA1 & VGA2 which both are in BB path */
|
||||||
|
osmosdr::gain_range_t bb_gains = get_gain_range( "VGA2", chan );
|
||||||
|
|
||||||
|
double clip_gain = bb_gains.clip( gain, true );
|
||||||
|
gain = set_gain( clip_gain, "VGA2", chan );
|
||||||
|
|
||||||
|
return gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector< std::string > bladerf_source_c::get_antennas( size_t chan )
|
||||||
|
{
|
||||||
|
std::vector< std::string > antennas;
|
||||||
|
|
||||||
|
antennas += get_antenna( chan );
|
||||||
|
|
||||||
|
return antennas;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bladerf_source_c::set_antenna( const std::string & antenna, size_t chan )
|
||||||
|
{
|
||||||
|
return get_antenna( chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string bladerf_source_c::get_antenna( size_t chan )
|
||||||
|
{
|
||||||
|
/* We only have a single receive antenna here */
|
||||||
|
return "RX";
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_source_c::set_dc_offset_mode( int mode, size_t chan )
|
||||||
|
{
|
||||||
|
if ( osmosdr_source_c::DCOffsetOff == mode ) {
|
||||||
|
//_src->set_auto_dc_offset( false, chan );
|
||||||
|
set_dc_offset( std::complex<double>(0.0, 0.0), chan ); /* reset to default for off-state */
|
||||||
|
} else if ( osmosdr_source_c::DCOffsetManual == mode ) {
|
||||||
|
//_src->set_auto_dc_offset( false, chan ); /* disable auto mode, but keep correcting with last known values */
|
||||||
|
} else if ( osmosdr_source_c::DCOffsetAutomatic == mode ) {
|
||||||
|
//_src->set_auto_dc_offset( true, chan );
|
||||||
|
std::cerr << "Automatic DC correction mode is not implemented." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_source_c::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = bladerf_common::set_dc_offset(BLADERF_MODULE_RX, offset, chan);
|
||||||
|
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set dc offset: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_source_c::set_iq_balance_mode( int mode, size_t chan )
|
||||||
|
{
|
||||||
|
if ( osmosdr_source_c::IQBalanceOff == mode ) {
|
||||||
|
//_src->set_auto_iq_balance( false, chan );
|
||||||
|
set_iq_balance( std::complex<double>(0.0, 0.0), chan ); /* reset to default for off-state */
|
||||||
|
} else if ( osmosdr_source_c::IQBalanceManual == mode ) {
|
||||||
|
//_src->set_auto_iq_balance( false, chan ); /* disable auto mode, but keep correcting with last known values */
|
||||||
|
} else if ( osmosdr_source_c::IQBalanceAutomatic == mode ) {
|
||||||
|
//_src->set_auto_iq_balance( true, chan );
|
||||||
|
std::cerr << "Automatic IQ correction mode is not implemented." << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bladerf_source_c::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
ret = bladerf_common::set_iq_balance(BLADERF_MODULE_RX, balance, chan);
|
||||||
|
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set iq balance: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
uint32_t actual;
|
||||||
|
|
||||||
|
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
||||||
|
bandwidth = get_sample_rate() * 0.75; /* select narrower filters to prevent aliasing */
|
||||||
|
|
||||||
|
ret = bladerf_set_bandwidth( _dev.get(), BLADERF_MODULE_RX, (uint32_t)bandwidth, &actual );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not set bandwidth: " +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_bandwidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
double bladerf_source_c::get_bandwidth( size_t chan )
|
||||||
|
{
|
||||||
|
uint32_t bandwidth;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = bladerf_get_bandwidth( _dev.get(), BLADERF_MODULE_RX, &bandwidth );
|
||||||
|
if( ret ) {
|
||||||
|
throw std::runtime_error( std::string(__FUNCTION__) + " " +
|
||||||
|
"could not get bandwidth:" +
|
||||||
|
std::string(bladerf_strerror(ret)) );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (double)bandwidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t bladerf_source_c::get_bandwidth_range( size_t chan )
|
||||||
|
{
|
||||||
|
return filter_bandwidths();
|
||||||
|
}
|
|
@ -0,0 +1,121 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Nuand LLC
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_BLADERF_SOURCE_C_H
|
||||||
|
#define INCLUDED_BLADERF_SOURCE_C_H
|
||||||
|
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
#include <gr_block.h>
|
||||||
|
#include <gr_sync_block.h>
|
||||||
|
|
||||||
|
#include "osmosdr/osmosdr_ranges.h"
|
||||||
|
#include "osmosdr_src_iface.h"
|
||||||
|
|
||||||
|
#include "bladerf_common.h"
|
||||||
|
|
||||||
|
class bladerf_source_c;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||||
|
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||||
|
* us transparent reference counting, which greatly simplifies storage
|
||||||
|
* management issues. This is especially helpful in our hybrid
|
||||||
|
* C++ / Python system.
|
||||||
|
*
|
||||||
|
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||||
|
*
|
||||||
|
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||||
|
*/
|
||||||
|
typedef boost::shared_ptr<bladerf_source_c> bladerf_source_c_sptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return a shared_ptr to a new instance of bladerf_source_c.
|
||||||
|
*
|
||||||
|
* To avoid accidental use of raw pointers, bladerf_source_c's
|
||||||
|
* constructor is private. bladerf_make_source_c is the public
|
||||||
|
* interface for creating new instances.
|
||||||
|
*/
|
||||||
|
bladerf_source_c_sptr make_bladerf_source_c (const std::string & args = "");
|
||||||
|
|
||||||
|
class bladerf_source_c :
|
||||||
|
public gr_sync_block,
|
||||||
|
public osmosdr_src_iface,
|
||||||
|
protected bladerf_common
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The friend declaration allows bladerf_make_source_c to
|
||||||
|
// access the private constructor.
|
||||||
|
friend bladerf_source_c_sptr make_bladerf_source_c (const std::string & args);
|
||||||
|
|
||||||
|
bladerf_source_c (const std::string & args); // private constructor
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
int work( int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items );
|
||||||
|
|
||||||
|
static std::vector< std::string > get_devices();
|
||||||
|
|
||||||
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
|
double set_sample_rate( double rate );
|
||||||
|
double get_sample_rate( void );
|
||||||
|
|
||||||
|
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
|
||||||
|
double set_center_freq( double freq, size_t chan = 0 );
|
||||||
|
double get_center_freq( size_t chan = 0 );
|
||||||
|
double set_freq_corr( double ppm, size_t chan = 0 );
|
||||||
|
double get_freq_corr( size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector<std::string> get_gain_names( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
|
||||||
|
bool set_gain_mode( bool automatic, size_t chan = 0 );
|
||||||
|
bool get_gain_mode( size_t chan = 0 );
|
||||||
|
double set_gain( double gain, size_t chan = 0 );
|
||||||
|
double set_gain( double gain, const std::string & name, size_t chan = 0 );
|
||||||
|
double get_gain( size_t chan = 0 );
|
||||||
|
double get_gain( const std::string & name, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bb_gain( double gain, size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector< std::string > get_antennas( size_t chan = 0 );
|
||||||
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_dc_offset_mode( int mode, size_t chan = 0 );
|
||||||
|
void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_iq_balance_mode( int mode, size_t chan = 0 );
|
||||||
|
void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
|
||||||
|
private:
|
||||||
|
osmosdr::gain_range_t _lna_range;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INCLUDED_BLADERF_SOURCE_C_H */
|
|
@ -12,5 +12,8 @@
|
||||||
#cmakedefine ENABLE_UHD
|
#cmakedefine ENABLE_UHD
|
||||||
#cmakedefine ENABLE_MIRI
|
#cmakedefine ENABLE_MIRI
|
||||||
#cmakedefine ENABLE_HACKRF
|
#cmakedefine ENABLE_HACKRF
|
||||||
|
#cmakedefine ENABLE_BLADERF
|
||||||
|
#cmakedefine ENABLE_RFSPACE
|
||||||
|
#cmakedefine ENABLE_AIRSPY
|
||||||
|
|
||||||
#endif // CONFIG_H_IN
|
#endif // CONFIG_H_IN
|
||||||
|
|
|
@ -21,10 +21,15 @@
|
||||||
# This file included, use CMake directory variables
|
# This file included, use CMake directory variables
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
||||||
include_directories(
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
|
||||||
${GNURADIO_FCD_INCLUDE_DIRS}
|
if(ENABLE_FCD)
|
||||||
)
|
include_directories(${GNURADIO_FCD_INCLUDE_DIRS})
|
||||||
|
endif(ENABLE_FCD)
|
||||||
|
|
||||||
|
if(ENABLE_FCDPP)
|
||||||
|
include_directories(${GNURADIO_FCDPP_INCLUDE_DIRS})
|
||||||
|
endif(ENABLE_FCDPP)
|
||||||
|
|
||||||
set(fcd_srcs
|
set(fcd_srcs
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/fcd_source.cc
|
${CMAKE_CURRENT_SOURCE_DIR}/fcd_source.cc
|
||||||
|
@ -34,5 +39,11 @@ set(fcd_srcs
|
||||||
# Append gnuradio-osmosdr library sources
|
# Append gnuradio-osmosdr library sources
|
||||||
########################################################################
|
########################################################################
|
||||||
list(APPEND gr_osmosdr_srcs ${fcd_srcs})
|
list(APPEND gr_osmosdr_srcs ${fcd_srcs})
|
||||||
list(APPEND gr_osmosdr_libs ${GNURADIO_FCD_LIBRARIES})
|
|
||||||
|
|
||||||
|
if(ENABLE_FCD)
|
||||||
|
list(APPEND gr_osmosdr_libs ${GNURADIO_FCD_LIBRARIES})
|
||||||
|
endif(ENABLE_FCD)
|
||||||
|
|
||||||
|
if(ENABLE_FCDPP)
|
||||||
|
list(APPEND gr_osmosdr_libs ${GNURADIO_FCDPP_LIBRARIES})
|
||||||
|
endif(ENABLE_FCDPP)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- c++ -*- */
|
/* -*- c++ -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2012 Dimitri Stolnikov <horiz0n@gmx.net>
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
*
|
*
|
||||||
* GNU Radio is free software; you can redistribute it and/or modify
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -42,10 +42,17 @@ fcd_source_sptr make_fcd_source(const std::string &args)
|
||||||
2 [V10 ]: USB-Audio - FUNcube Dongle V1.0
|
2 [V10 ]: USB-Audio - FUNcube Dongle V1.0
|
||||||
Hanlincrest Ltd. FUNcube Dongle V1.0 at usb-0000:00:1d.0-2, full speed
|
Hanlincrest Ltd. FUNcube Dongle V1.0 at usb-0000:00:1d.0-2, full speed
|
||||||
*/
|
*/
|
||||||
|
/*
|
||||||
|
2 [V20 ]: USB-Audio - FUNcube Dongle V2.0
|
||||||
|
Hanlincrest Ltd. FUNcube Dongle V2.0 at usb-0000:00:1d.0-2, full speed
|
||||||
|
*/
|
||||||
|
|
||||||
static std::vector< std::string > _get_devices()
|
typedef std::pair< fcd_source::dongle_type, std::string > device_t;
|
||||||
|
typedef std::vector< device_t > devices_t;
|
||||||
|
|
||||||
|
static devices_t _get_devices() /* FIXME: non-portable way to discover dongles */
|
||||||
{
|
{
|
||||||
std::vector< std::string > devices;
|
devices_t devices;
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
std::ifstream cards( "/proc/asound/cards" );
|
std::ifstream cards( "/proc/asound/cards" );
|
||||||
|
@ -55,15 +62,23 @@ static std::vector< std::string > _get_devices()
|
||||||
{
|
{
|
||||||
getline (cards, line);
|
getline (cards, line);
|
||||||
|
|
||||||
if ( line.find( "USB-Audio - FUNcube Dongle" ) != std::string::npos )
|
fcd_source::dongle_type type = fcd_source::FUNCUBE_UNKNOWN;
|
||||||
|
|
||||||
|
if ( line.find( "USB-Audio - FUNcube Dongle V1.0" ) != std::string::npos )
|
||||||
|
type = fcd_source::FUNCUBE_V1;
|
||||||
|
|
||||||
|
if ( line.find( "USB-Audio - FUNcube Dongle V2.0" ) != std::string::npos )
|
||||||
|
type = fcd_source::FUNCUBE_V2;
|
||||||
|
|
||||||
|
if ( type != fcd_source::FUNCUBE_UNKNOWN )
|
||||||
{
|
{
|
||||||
int id;
|
int id;
|
||||||
std::istringstream( line ) >> id;
|
std::istringstream( line ) >> id;
|
||||||
|
|
||||||
std::ostringstream hw_id;
|
std::ostringstream hw_id;
|
||||||
hw_id << "hw:" << id; // build alsa identifier
|
hw_id << "hw:" << id; /* build alsa identifier */
|
||||||
|
|
||||||
devices += hw_id.str();
|
devices += device_t( type, hw_id.str() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +91,8 @@ static std::vector< std::string > _get_devices()
|
||||||
fcd_source::fcd_source(const std::string &args) :
|
fcd_source::fcd_source(const std::string &args) :
|
||||||
gr_hier_block2("fcd_source",
|
gr_hier_block2("fcd_source",
|
||||||
gr_make_io_signature (0, 0, 0),
|
gr_make_io_signature (0, 0, 0),
|
||||||
gr_make_io_signature (1, 1, sizeof (gr_complex)))
|
gr_make_io_signature (1, 1, sizeof (gr_complex))),
|
||||||
|
_type( FUNCUBE_UNKNOWN )
|
||||||
{
|
{
|
||||||
std::string dev_name;
|
std::string dev_name;
|
||||||
unsigned int dev_index = 0;
|
unsigned int dev_index = 0;
|
||||||
|
@ -84,18 +100,70 @@ fcd_source::fcd_source(const std::string &args) :
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
if (dict.count("fcd"))
|
if (dict.count("fcd"))
|
||||||
dev_index = boost::lexical_cast< unsigned int >( dict["fcd"] );
|
{
|
||||||
|
std::string value = dict["fcd"];
|
||||||
|
if ( value.length() )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
dev_index = boost::lexical_cast< unsigned int >( value );
|
||||||
|
} catch ( std::exception &ex ) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Failed to use '" + value + "' as index: " + ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector< std::string > devices = _get_devices();
|
if (dict.count("device"))
|
||||||
|
{
|
||||||
|
dev_name = dict["device"];
|
||||||
|
_type = FUNCUBE_V1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict.count("type"))
|
||||||
|
{
|
||||||
|
_type = (dongle_type) boost::lexical_cast< int >( dict["type"] );
|
||||||
|
|
||||||
|
if ( FUNCUBE_V1 != _type && FUNCUBE_V2 != _type )
|
||||||
|
throw std::runtime_error("FUNcube Dongle type must be 1 or 2.");
|
||||||
|
}
|
||||||
|
|
||||||
|
devices_t devices = _get_devices();
|
||||||
|
|
||||||
if ( devices.size() )
|
if ( devices.size() )
|
||||||
dev_name = devices[dev_index];
|
{
|
||||||
else
|
if ( FUNCUBE_UNKNOWN == _type )
|
||||||
throw std::runtime_error("No FunCube Dongle found.");
|
_type = devices[dev_index].first;
|
||||||
|
|
||||||
_src = fcd_make_source_c( dev_name );
|
if ( dev_name.length() == 0 )
|
||||||
|
dev_name = devices[dev_index].second;
|
||||||
|
}
|
||||||
|
else if ( dev_name.length() == 0 )
|
||||||
|
throw std::runtime_error("No FUNcube Dongle found.");
|
||||||
|
|
||||||
connect( _src, 0, self(), 0 );
|
std::cerr << "Using " << name() << " (" << dev_name << ")" << std::endl;
|
||||||
|
|
||||||
|
#ifdef HAVE_FCD
|
||||||
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
{
|
||||||
|
_src_v1 = fcd_make_source_c( dev_name );
|
||||||
|
connect( _src_v1, 0, self(), 0 );
|
||||||
|
|
||||||
|
set_gain( 20, "LNA" );
|
||||||
|
set_gain( 12, "MIX" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
{
|
||||||
|
_src_v2 = gr::fcdproplus::fcdproplus::make( dev_name );
|
||||||
|
connect( _src_v2, 0, self(), 0 );
|
||||||
|
|
||||||
|
set_gain( 1, "LNA" );
|
||||||
|
set_gain( 1, "MIX" );
|
||||||
|
set_gain( 15, "BB" );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
fcd_source::~fcd_source()
|
fcd_source::~fcd_source()
|
||||||
|
@ -107,9 +175,15 @@ std::vector< std::string > fcd_source::get_devices()
|
||||||
int id = 0;
|
int id = 0;
|
||||||
std::vector< std::string > devices;
|
std::vector< std::string > devices;
|
||||||
|
|
||||||
BOOST_FOREACH( std::string dev, _get_devices() ) {
|
BOOST_FOREACH( device_t dev, _get_devices() )
|
||||||
|
{
|
||||||
std::string args = "fcd=" + boost::lexical_cast< std::string >( id++ );
|
std::string args = "fcd=" + boost::lexical_cast< std::string >( id++ );
|
||||||
args += ",label='FunCube Dongle'";
|
|
||||||
|
if ( dev.first == fcd_source::FUNCUBE_V1 )
|
||||||
|
args += ",label='FUNcube Dongle V1.0'";
|
||||||
|
else if ( dev.first == fcd_source::FUNCUBE_V2 )
|
||||||
|
args += ",label='FUNcube Dongle V2.0'";
|
||||||
|
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,7 +192,12 @@ std::vector< std::string > fcd_source::get_devices()
|
||||||
|
|
||||||
std::string fcd_source::name()
|
std::string fcd_source::name()
|
||||||
{
|
{
|
||||||
return "FUNcube Dongle";
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
return "FUNcube Dongle V1.0";
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
return "FUNcube Dongle V2.0";
|
||||||
|
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t fcd_source::get_num_channels( void )
|
size_t fcd_source::get_num_channels( void )
|
||||||
|
@ -142,19 +221,35 @@ double fcd_source::set_sample_rate( double rate )
|
||||||
|
|
||||||
double fcd_source::get_sample_rate( void )
|
double fcd_source::get_sample_rate( void )
|
||||||
{
|
{
|
||||||
|
if ( FUNCUBE_V1 == _type )
|
||||||
return 96e3;
|
return 96e3;
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
return 192e3;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
osmosdr::freq_range_t fcd_source::get_freq_range( size_t chan )
|
osmosdr::freq_range_t fcd_source::get_freq_range( size_t chan )
|
||||||
{
|
{
|
||||||
osmosdr::freq_range_t range( 52e6, 2.2e9 );
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
return osmosdr::freq_range_t( 52e6, 2.2e9 );
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
return osmosdr::freq_range_t( 150e3, 2.05e9 );
|
||||||
|
|
||||||
return range;
|
return osmosdr::freq_range_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
double fcd_source::set_center_freq( double freq, size_t chan )
|
double fcd_source::set_center_freq( double freq, size_t chan )
|
||||||
{
|
{
|
||||||
_src->set_freq(float(freq));
|
#ifdef HAVE_FCD
|
||||||
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
_src_v1->set_freq( float(freq) );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
_src_v2->set_freq( float(freq) );
|
||||||
|
#endif
|
||||||
|
|
||||||
_freq = freq;
|
_freq = freq;
|
||||||
|
|
||||||
|
@ -168,7 +263,15 @@ double fcd_source::get_center_freq( size_t chan )
|
||||||
|
|
||||||
double fcd_source::set_freq_corr( double ppm, size_t chan )
|
double fcd_source::set_freq_corr( double ppm, size_t chan )
|
||||||
{
|
{
|
||||||
_src->set_freq_corr( ppm );
|
#ifdef HAVE_FCD
|
||||||
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
_src_v1->set_freq_corr( ppm );
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
_src_v2->set_freq_corr( ppm );
|
||||||
|
#endif
|
||||||
|
|
||||||
_correct = ppm;
|
_correct = ppm;
|
||||||
|
|
||||||
|
@ -185,44 +288,131 @@ std::vector<std::string> fcd_source::get_gain_names( size_t chan )
|
||||||
std::vector< std::string > names;
|
std::vector< std::string > names;
|
||||||
|
|
||||||
names += "LNA";
|
names += "LNA";
|
||||||
|
names += "MIX";
|
||||||
|
|
||||||
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
names += "BB";
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
osmosdr::gain_range_t fcd_source::get_gain_range( size_t chan )
|
osmosdr::gain_range_t fcd_source::get_gain_range( size_t chan )
|
||||||
{
|
{
|
||||||
osmosdr::gain_range_t range(-5, 30, 2.5);
|
std::string name = "";
|
||||||
|
|
||||||
return range;
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
name = "LNA"; /* use LNA gain for V1 dongle */
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
name = "BB"; /* use BB gain for V2 dongle */
|
||||||
|
|
||||||
|
return get_gain_range( name, chan );
|
||||||
}
|
}
|
||||||
|
|
||||||
osmosdr::gain_range_t fcd_source::get_gain_range( const std::string & name, size_t chan )
|
osmosdr::gain_range_t fcd_source::get_gain_range( const std::string & name, size_t chan )
|
||||||
{
|
{
|
||||||
return get_gain_range( chan );
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
return osmosdr::gain_range_t(-5, 30, 2.5);
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
return osmosdr::gain_range_t(4, 12, 8);
|
||||||
|
}
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
return osmosdr::gain_range_t(0, 1, 1);
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
return osmosdr::gain_range_t(0, 1, 1);
|
||||||
|
else if ( "BB" == name )
|
||||||
|
return osmosdr::gain_range_t(0, 59, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return osmosdr::gain_range_t();
|
||||||
}
|
}
|
||||||
|
|
||||||
double fcd_source::set_gain( double gain, size_t chan )
|
double fcd_source::set_gain( double gain, size_t chan )
|
||||||
{
|
{
|
||||||
_src->set_lna_gain(gain);
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
_lna_gain = set_gain( gain, "LNA" );
|
||||||
|
|
||||||
_gain = gain;
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
_bb_gain = set_gain( gain, "BB" );
|
||||||
|
|
||||||
return get_gain(chan);
|
return get_gain(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
double fcd_source::set_gain( double gain, const std::string & name, size_t chan )
|
double fcd_source::set_gain( double gain, const std::string & name, size_t chan )
|
||||||
{
|
{
|
||||||
return set_gain(chan);
|
#ifdef HAVE_FCD
|
||||||
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
{
|
||||||
|
_lna_gain = gain;
|
||||||
|
_src_v1->set_lna_gain(_lna_gain);
|
||||||
|
}
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
{
|
||||||
|
_mix_gain = gain > 4 ? 12 : 4;
|
||||||
|
_src_v1->set_mixer_gain(_mix_gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
if ( FUNCUBE_V2 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
{
|
||||||
|
_lna_gain = gain > 0 ? 1 : 0;
|
||||||
|
_src_v2->set_lna(_lna_gain);
|
||||||
|
}
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
{
|
||||||
|
_mix_gain = gain > 0 ? 1 : 0;
|
||||||
|
_src_v2->set_mixer_gain(_mix_gain);
|
||||||
|
}
|
||||||
|
else if ( "BB" == name )
|
||||||
|
{
|
||||||
|
_bb_gain = gain;
|
||||||
|
_src_v2->set_if_gain(_bb_gain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return get_gain( name, chan );
|
||||||
}
|
}
|
||||||
|
|
||||||
double fcd_source::get_gain( size_t chan )
|
double fcd_source::get_gain( size_t chan )
|
||||||
{
|
{
|
||||||
return _gain;
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
return get_gain( "LNA", chan );
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
return get_gain( "BB", chan );
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
double fcd_source::get_gain( const std::string & name, size_t chan )
|
double fcd_source::get_gain( const std::string & name, size_t chan )
|
||||||
{
|
{
|
||||||
return get_gain(chan);
|
if ( FUNCUBE_V1 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
return _lna_gain;
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
return _mix_gain;
|
||||||
|
}
|
||||||
|
else if ( FUNCUBE_V2 == _type )
|
||||||
|
{
|
||||||
|
if ( "LNA" == name )
|
||||||
|
return _lna_gain;
|
||||||
|
else if ( "MIX" == name )
|
||||||
|
return _mix_gain;
|
||||||
|
else if ( "BB" == name )
|
||||||
|
return _bb_gain;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector< std::string > fcd_source::get_antennas( size_t chan )
|
std::vector< std::string > fcd_source::get_antennas( size_t chan )
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* -*- c++ -*- */
|
/* -*- c++ -*- */
|
||||||
/*
|
/*
|
||||||
* Copyright 2012 Dimitri Stolnikov <horiz0n@gmx.net>
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
*
|
*
|
||||||
* GNU Radio is free software; you can redistribute it and/or modify
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
@ -22,7 +22,13 @@
|
||||||
|
|
||||||
#include <gr_hier_block2.h>
|
#include <gr_hier_block2.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_FCD
|
||||||
#include <fcd_source_c.h>
|
#include <fcd_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
#include <fcdproplus/fcdproplus.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "osmosdr_src_iface.h"
|
#include "osmosdr_src_iface.h"
|
||||||
|
|
||||||
|
@ -44,6 +50,12 @@ private:
|
||||||
public:
|
public:
|
||||||
~fcd_source();
|
~fcd_source();
|
||||||
|
|
||||||
|
enum dongle_type {
|
||||||
|
FUNCUBE_UNKNOWN,
|
||||||
|
FUNCUBE_V1,
|
||||||
|
FUNCUBE_V2
|
||||||
|
};
|
||||||
|
|
||||||
static std::vector< std::string > get_devices();
|
static std::vector< std::string > get_devices();
|
||||||
|
|
||||||
std::string name();
|
std::string name();
|
||||||
|
@ -73,9 +85,15 @@ public:
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
fcd_source_c_sptr _src;
|
dongle_type _type;
|
||||||
double _gain, _freq;
|
#ifdef HAVE_FCD
|
||||||
int32_t _correct;
|
fcd_source_c_sptr _src_v1;
|
||||||
|
#endif
|
||||||
|
#ifdef HAVE_FCDPP
|
||||||
|
gr::fcdproplus::fcdproplus::sptr _src_v2;
|
||||||
|
#endif
|
||||||
|
double _lna_gain, _mix_gain, _bb_gain, _freq;
|
||||||
|
int _correct;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // FCD_SOURCE_H
|
#endif // FCD_SOURCE_H
|
||||||
|
|
|
@ -23,10 +23,9 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#include <boost/assign.hpp>
|
#include <boost/assign.hpp>
|
||||||
|
#include <boost/format.hpp>
|
||||||
|
|
||||||
#include <gr_io_signature.h>
|
#include <gr_io_signature.h>
|
||||||
#include <gr_file_source.h>
|
|
||||||
#include <gr_throttle.h>
|
|
||||||
|
|
||||||
#include "file_source_c.h"
|
#include "file_source_c.h"
|
||||||
|
|
||||||
|
@ -73,20 +72,22 @@ file_source_c::file_source_c(const std::string &args) :
|
||||||
if (_freq < 0)
|
if (_freq < 0)
|
||||||
throw std::runtime_error("Parameter 'freq' may not be negative.");
|
throw std::runtime_error("Parameter 'freq' may not be negative.");
|
||||||
|
|
||||||
if (0 == _rate)
|
if (0 == _rate && throttle)
|
||||||
throw std::runtime_error("Parameter 'rate' is missing in arguments.");
|
throw std::runtime_error("Parameter 'rate' is missing in arguments.");
|
||||||
|
|
||||||
gr_file_source_sptr src = gr_make_file_source( sizeof(gr_complex),
|
_file_rate = _rate;
|
||||||
|
|
||||||
|
_source = gr_make_file_source( sizeof(gr_complex),
|
||||||
filename.c_str(),
|
filename.c_str(),
|
||||||
repeat );
|
repeat );
|
||||||
|
|
||||||
if (throttle) {
|
_throttle = gr_make_throttle( sizeof(gr_complex), _file_rate );
|
||||||
gr_throttle::sptr throttle = gr_make_throttle( sizeof(gr_complex), _rate );
|
|
||||||
|
|
||||||
connect( src, 0, throttle, 0 );
|
if (throttle) {
|
||||||
connect( throttle, 0, self(), 0 );
|
connect( _source, 0, _throttle, 0 );
|
||||||
|
connect( _throttle, 0, self(), 0 );
|
||||||
} else {
|
} else {
|
||||||
connect( src, 0, self(), 0 );
|
connect( _source, 0, self(), 0 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +100,17 @@ std::string file_source_c::name()
|
||||||
return "IQ File Source";
|
return "IQ File Source";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> file_source_c::get_devices()
|
std::vector<std::string> file_source_c::get_devices( bool fake )
|
||||||
{
|
{
|
||||||
std::vector<std::string> devices;
|
std::vector<std::string> devices;
|
||||||
|
|
||||||
std::string args = "file='/path/to/your/file',rate=1e6,freq=100e6,repeat=true,throttle=true";
|
if ( fake )
|
||||||
|
{
|
||||||
|
std::string args = "file='/path/to/your/file'";
|
||||||
|
args += ",rate=1e6,freq=100e6,repeat=true,throttle=true";
|
||||||
args += ",label='Complex Sampled (IQ) File'";
|
args += ",label='Complex Sampled (IQ) File'";
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
|
}
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
@ -115,17 +120,33 @@ size_t file_source_c::get_num_channels( void )
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool file_source_c::seek( long seek_point, int whence , size_t chan )
|
||||||
|
{
|
||||||
|
return _source->seek( seek_point, whence );
|
||||||
|
}
|
||||||
|
|
||||||
osmosdr::meta_range_t file_source_c::get_sample_rates( void )
|
osmosdr::meta_range_t file_source_c::get_sample_rates( void )
|
||||||
{
|
{
|
||||||
osmosdr::meta_range_t range;
|
osmosdr::meta_range_t range;
|
||||||
|
|
||||||
range += osmosdr::range_t( get_sample_rate() );
|
range += osmosdr::range_t( _file_rate ); /* always return file's original rate */
|
||||||
|
|
||||||
return range;
|
return range;
|
||||||
}
|
}
|
||||||
|
|
||||||
double file_source_c::set_sample_rate( double rate )
|
double file_source_c::set_sample_rate( double rate )
|
||||||
{
|
{
|
||||||
|
if ( _file_rate != rate )
|
||||||
|
{
|
||||||
|
std::cerr << boost::format("WARNING: Overriding original sample rate of %g with %g")
|
||||||
|
% _file_rate % rate
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
_throttle->set_sample_rate( rate );
|
||||||
|
|
||||||
|
_rate = rate;
|
||||||
|
|
||||||
return get_sample_rate();
|
return get_sample_rate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,8 @@
|
||||||
#define FILE_SOURCE_C_H
|
#define FILE_SOURCE_C_H
|
||||||
|
|
||||||
#include <gr_hier_block2.h>
|
#include <gr_hier_block2.h>
|
||||||
|
#include <gr_file_source.h>
|
||||||
|
#include <gr_throttle.h>
|
||||||
|
|
||||||
#include "osmosdr_src_iface.h"
|
#include "osmosdr_src_iface.h"
|
||||||
|
|
||||||
|
@ -44,10 +46,12 @@ public:
|
||||||
|
|
||||||
std::string name();
|
std::string name();
|
||||||
|
|
||||||
static std::vector< std::string > get_devices();
|
static std::vector< std::string > get_devices( bool fake = false );
|
||||||
|
|
||||||
size_t get_num_channels( void );
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
bool seek( long seek_point, int whence, size_t chan );
|
||||||
|
|
||||||
osmosdr::meta_range_t get_sample_rates( void );
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
double set_sample_rate( double rate );
|
double set_sample_rate( double rate );
|
||||||
double get_sample_rate( void );
|
double get_sample_rate( void );
|
||||||
|
@ -71,6 +75,10 @@ public:
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
gr_file_source_sptr _source;
|
||||||
|
gr_throttle::sptr _throttle;
|
||||||
|
|
||||||
|
double _file_rate;
|
||||||
double _freq, _rate;
|
double _freq, _rate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -51,10 +51,18 @@
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
||||||
#define BUF_NUM 32
|
#define BUF_NUM 15
|
||||||
|
|
||||||
#define BYTES_PER_SAMPLE 2 /* HackRF device consumes 8 bit unsigned IQ data */
|
#define BYTES_PER_SAMPLE 2 /* HackRF device consumes 8 bit unsigned IQ data */
|
||||||
|
|
||||||
|
#define HACKRF_THROW_ON_ERROR(ret, msg) \
|
||||||
|
if ( ret != HACKRF_SUCCESS ) \
|
||||||
|
throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
|
||||||
|
% ret % hackrf_error_name((enum hackrf_error)ret) ) );
|
||||||
|
|
||||||
|
#define HACKRF_FUNC_STR(func, arg) \
|
||||||
|
boost::str(boost::format(func "(%d)") % arg) + " has failed"
|
||||||
|
|
||||||
static inline bool cb_init(circular_buffer_t *cb, size_t capacity, size_t sz)
|
static inline bool cb_init(circular_buffer_t *cb, size_t capacity, size_t sz)
|
||||||
{
|
{
|
||||||
cb->buffer = malloc(capacity * sz);
|
cb->buffer = malloc(capacity * sz);
|
||||||
|
@ -130,7 +138,7 @@ hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args)
|
||||||
* output signatures are used by the runtime system to
|
* output signatures are used by the runtime system to
|
||||||
* check that a valid number and type of inputs and outputs
|
* check that a valid number and type of inputs and outputs
|
||||||
* are connected to this block. In this case, we accept
|
* are connected to this block. In this case, we accept
|
||||||
* only 0 input and 1 output.
|
* only 1 input and 0 output.
|
||||||
*/
|
*/
|
||||||
static const int MIN_IN = 1; // mininum number of input streams
|
static const int MIN_IN = 1; // mininum number of input streams
|
||||||
static const int MAX_IN = 1; // maximum number of input streams
|
static const int MAX_IN = 1; // maximum number of input streams
|
||||||
|
@ -151,10 +159,10 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
|
||||||
_freq_corr(0),
|
_freq_corr(0),
|
||||||
_auto_gain(false),
|
_auto_gain(false),
|
||||||
_amp_gain(0),
|
_amp_gain(0),
|
||||||
_vga_gain(0)
|
_vga_gain(0),
|
||||||
|
_bandwidth(0)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
uint16_t val;
|
|
||||||
|
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
|
@ -177,24 +185,20 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
|
||||||
|
|
||||||
_dev = NULL;
|
_dev = NULL;
|
||||||
ret = hackrf_open( &_dev );
|
ret = hackrf_open( &_dev );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device")
|
||||||
throw std::runtime_error("Failed to open HackRF device.");
|
|
||||||
|
|
||||||
uint8_t board_id;
|
uint8_t board_id;
|
||||||
ret = hackrf_board_id_read( _dev, &board_id );
|
ret = hackrf_board_id_read( _dev, &board_id );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id")
|
||||||
throw std::runtime_error("Failed to get board id.");
|
|
||||||
|
|
||||||
char version[40];
|
char version[40];
|
||||||
memset(version, 0, sizeof(version));
|
memset(version, 0, sizeof(version));
|
||||||
ret = hackrf_version_string_read( _dev, version, sizeof(version));
|
ret = hackrf_version_string_read( _dev, version, sizeof(version));
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to read version string")
|
||||||
throw std::runtime_error("Failed to read version string.");
|
|
||||||
#if 0
|
#if 0
|
||||||
read_partid_serialno_t serial_number;
|
read_partid_serialno_t serial_number;
|
||||||
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
|
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number")
|
||||||
throw std::runtime_error("Failed to read serial number.");
|
|
||||||
#endif
|
#endif
|
||||||
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
|
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
|
||||||
<< "with firmware " << version << " "
|
<< "with firmware " << version << " "
|
||||||
|
@ -205,21 +209,22 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_sample_rate( 5000000 );
|
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
|
||||||
|
set_sample_rate( get_sample_rates().start() );
|
||||||
|
set_bandwidth( 0 );
|
||||||
|
|
||||||
set_gain( 0 ); /* disable AMP gain stage */
|
set_gain( 14 ); /* enable AMP gain stage by default */
|
||||||
|
|
||||||
hackrf_max2837_read( _dev, 29, &val );
|
|
||||||
val |= 0x3; /* enable TX VGA control over SPI */
|
|
||||||
hackrf_max2837_write( _dev, 29, val );
|
|
||||||
|
|
||||||
set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */
|
set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */
|
||||||
|
|
||||||
_buf = (unsigned char *) malloc( BUF_LEN );
|
_buf = (char *) malloc( BUF_LEN );
|
||||||
|
|
||||||
cb_init( &_cbuf, _buf_num, BUF_LEN );
|
cb_init( &_cbuf, _buf_num, BUF_LEN );
|
||||||
|
|
||||||
// _thread = gruel::thread(_hackrf_wait, this);
|
// _thread = gruel::thread(_hackrf_wait, this);
|
||||||
|
|
||||||
|
ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to start TX streaming")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -229,7 +234,10 @@ hackrf_sink_c::~hackrf_sink_c ()
|
||||||
{
|
{
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
// _thread.join();
|
// _thread.join();
|
||||||
hackrf_close( _dev );
|
int ret = hackrf_stop_tx( _dev );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to stop TX streaming")
|
||||||
|
ret = hackrf_close( _dev );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to close HackRF")
|
||||||
_dev = NULL;
|
_dev = NULL;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -292,13 +300,13 @@ bool hackrf_sink_c::start()
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
_buf_used = 0;
|
_buf_used = 0;
|
||||||
|
#if 0
|
||||||
int ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this );
|
int ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this );
|
||||||
if ( ret != HACKRF_SUCCESS ) {
|
if ( ret != HACKRF_SUCCESS ) {
|
||||||
std::cerr << "Failed to start TX streaming (" << ret << ")" << std::endl;
|
std::cerr << "Failed to start TX streaming (" << ret << ")" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,22 +314,20 @@ bool hackrf_sink_c::stop()
|
||||||
{
|
{
|
||||||
if ( ! _dev )
|
if ( ! _dev )
|
||||||
return false;
|
return false;
|
||||||
|
#if 0
|
||||||
int ret = hackrf_stop_tx( _dev );
|
int ret = hackrf_stop_tx( _dev );
|
||||||
if ( ret != HACKRF_SUCCESS ) {
|
if ( ret != HACKRF_SUCCESS ) {
|
||||||
std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl;
|
std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_AVX
|
#ifdef USE_AVX
|
||||||
void convert_avx(const float* inbuf, unsigned char* outbuf,const unsigned int count)
|
void convert_avx(const float* inbuf, char* outbuf,const unsigned int count)
|
||||||
{
|
{
|
||||||
__m256 mulme = _mm256_set_ps(127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f);
|
__m256 mulme = _mm256_set_ps(127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f, 127.0f);
|
||||||
__m128i addme = _mm_set_epi16(127, 127, 127, 127, 127, 127, 127, 127);
|
|
||||||
|
|
||||||
for(unsigned int i=0; i<count;i++){
|
for(unsigned int i=0; i<count;i++){
|
||||||
|
|
||||||
__m256i itmp3 = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_loadu_ps(&inbuf[i*16+0]), mulme));
|
__m256i itmp3 = _mm256_cvtps_epi32(_mm256_mul_ps(_mm256_loadu_ps(&inbuf[i*16+0]), mulme));
|
||||||
|
@ -332,19 +338,19 @@ void convert_avx(const float* inbuf, unsigned char* outbuf,const unsigned int co
|
||||||
__m128i a3 = _mm256_extractf128_si256(itmp4, 1);
|
__m128i a3 = _mm256_extractf128_si256(itmp4, 1);
|
||||||
__m128i a2 = _mm256_castsi256_si128(itmp4);
|
__m128i a2 = _mm256_castsi256_si128(itmp4);
|
||||||
|
|
||||||
__m128i outshorts1 = _mm_add_epi16(_mm_packs_epi32(a0, a1), addme);
|
__m128i outshorts1 = _mm_packs_epi32(a0, a1);
|
||||||
__m128i outshorts2 = _mm_add_epi16(_mm_packs_epi32(a2, a3), addme);
|
__m128i outshorts2 = _mm_packs_epi32(a2, a3);
|
||||||
__m128i outbytes = _mm_packus_epi16(outshorts1, outshorts2);
|
|
||||||
|
__m128i outbytes = _mm_packs_epi16(outshorts1, outshorts2);
|
||||||
|
|
||||||
_mm_storeu_si128 ((__m128i*)&outbuf[i*16], outbytes);
|
_mm_storeu_si128 ((__m128i*)&outbuf[i*16], outbytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#elif USE_SSE2
|
#elif USE_SSE2
|
||||||
void convert_sse2(const float* inbuf, unsigned char* outbuf,const unsigned int count)
|
void convert_sse2(const float* inbuf, char* outbuf,const unsigned int count)
|
||||||
{
|
{
|
||||||
const register __m128 mulme = _mm_set_ps( 127.0f, 127.0f, 127.0f, 127.0f );
|
const register __m128 mulme = _mm_set_ps( 127.0f, 127.0f, 127.0f, 127.0f );
|
||||||
__m128i addme = _mm_set_epi16( 127, 127, 127, 127, 127, 127, 127, 127);
|
|
||||||
__m128 itmp1,itmp2,itmp3,itmp4;
|
__m128 itmp1,itmp2,itmp3,itmp4;
|
||||||
__m128i otmp1,otmp2,otmp3,otmp4;
|
__m128i otmp1,otmp2,otmp3,otmp4;
|
||||||
|
|
||||||
|
@ -363,20 +369,20 @@ void convert_sse2(const float* inbuf, unsigned char* outbuf,const unsigned int c
|
||||||
otmp3 = _mm_cvtps_epi32(itmp3);
|
otmp3 = _mm_cvtps_epi32(itmp3);
|
||||||
otmp4 = _mm_cvtps_epi32(itmp4);
|
otmp4 = _mm_cvtps_epi32(itmp4);
|
||||||
|
|
||||||
outshorts1 = _mm_add_epi16(_mm_packs_epi32(otmp1, otmp2), addme);
|
outshorts1 = _mm_packs_epi32(otmp1, otmp2);
|
||||||
outshorts2 = _mm_add_epi16(_mm_packs_epi32(otmp3, otmp4), addme);
|
outshorts2 = _mm_packs_epi32(otmp3, otmp4);
|
||||||
|
|
||||||
outbytes = _mm_packus_epi16(outshorts1, outshorts2);
|
outbytes = _mm_packs_epi16(outshorts1, outshorts2);
|
||||||
|
|
||||||
_mm_storeu_si128 ((__m128i*)&outbuf[i*16], outbytes);
|
_mm_storeu_si128 ((__m128i*)&outbuf[i*16], outbytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void convert_default(float* inbuf, unsigned char* outbuf,const unsigned int count)
|
void convert_default(float* inbuf, char* outbuf,const unsigned int count)
|
||||||
{
|
{
|
||||||
for(unsigned int i=0; i<count;i++){
|
for(unsigned int i=0; i<count;i++){
|
||||||
outbuf[i]= inbuf[i]*127+127;
|
outbuf[i]= inbuf[i]*127;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,7 +399,7 @@ int hackrf_sink_c::work( int noutput_items,
|
||||||
_buf_cond.wait( lock );
|
_buf_cond.wait( lock );
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char *buf = _buf + _buf_used;
|
char *buf = _buf + _buf_used;
|
||||||
unsigned int prev_buf_used = _buf_used;
|
unsigned int prev_buf_used = _buf_used;
|
||||||
|
|
||||||
unsigned int remaining = (BUF_LEN-_buf_used)/2; //complex
|
unsigned int remaining = (BUF_LEN-_buf_used)/2; //complex
|
||||||
|
@ -442,7 +448,7 @@ std::vector<std::string> hackrf_sink_c::get_devices()
|
||||||
{
|
{
|
||||||
std::vector<std::string> devices;
|
std::vector<std::string> devices;
|
||||||
std::string label;
|
std::string label;
|
||||||
|
#if 0
|
||||||
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; i++) {
|
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; i++) {
|
||||||
std::string args = "hackrf=" + boost::lexical_cast< std::string >( i );
|
std::string args = "hackrf=" + boost::lexical_cast< std::string >( i );
|
||||||
|
|
||||||
|
@ -455,7 +461,49 @@ std::vector<std::string> hackrf_sink_c::get_devices()
|
||||||
args += ",label='" + label + "'";
|
args += ",label='" + label + "'";
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
hackrf_init(); /* call only once before the first open */
|
||||||
|
|
||||||
|
_usage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
hackrf_device *dev = NULL;
|
||||||
|
ret = hackrf_open(&dev);
|
||||||
|
if ( HACKRF_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
std::string args = "hackrf=0";
|
||||||
|
|
||||||
|
label = "HackRF";
|
||||||
|
|
||||||
|
uint8_t board_id;
|
||||||
|
ret = hackrf_board_id_read( dev, &board_id );
|
||||||
|
if ( HACKRF_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
label += std::string(" ") + hackrf_board_id_name(hackrf_board_id(board_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
args += ",label='" + label + "'";
|
||||||
|
devices.push_back( args );
|
||||||
|
|
||||||
|
ret = hackrf_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
_usage--;
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
hackrf_exit(); /* call only once after last close */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +516,11 @@ osmosdr::meta_range_t hackrf_sink_c::get_sample_rates()
|
||||||
{
|
{
|
||||||
osmosdr::meta_range_t range;
|
osmosdr::meta_range_t range;
|
||||||
|
|
||||||
range += osmosdr::range_t( 5e6 ); /* out of spec but appears to work */
|
/* we only add integer rates here because of better phase noise performance.
|
||||||
|
* the user is allowed to request arbitrary (fractional) rates within these
|
||||||
|
* boundaries. */
|
||||||
|
|
||||||
|
range += osmosdr::range_t( 8e6 );
|
||||||
range += osmosdr::range_t( 10e6 );
|
range += osmosdr::range_t( 10e6 );
|
||||||
range += osmosdr::range_t( 12.5e6 );
|
range += osmosdr::range_t( 12.5e6 );
|
||||||
range += osmosdr::range_t( 16e6 );
|
range += osmosdr::range_t( 16e6 );
|
||||||
|
@ -482,12 +534,12 @@ double hackrf_sink_c::set_sample_rate(double rate)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
ret = hackrf_sample_rate_set( _dev, uint32_t(rate) );
|
ret = hackrf_set_sample_rate( _dev, rate );
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_sample_rate = rate;
|
_sample_rate = rate;
|
||||||
set_bandwidth( rate );
|
//set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_sample_rate", rate ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,7 +572,7 @@ double hackrf_sink_c::set_center_freq( double freq, size_t chan )
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_center_freq = freq;
|
_center_freq = freq;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_freq", corr_freq ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,25 +640,18 @@ bool hackrf_sink_c::get_gain_mode( size_t chan )
|
||||||
|
|
||||||
double hackrf_sink_c::set_gain( double gain, size_t chan )
|
double hackrf_sink_c::set_gain( double gain, size_t chan )
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
|
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
double clip_gain = rf_gains.clip( gain, true );
|
double clip_gain = rf_gains.clip( gain, true );
|
||||||
|
uint8_t value = clip_gain == 14.0f ? 1 : 0;
|
||||||
|
|
||||||
std::map<double, int> reg_vals;
|
ret = hackrf_set_amp_enable( _dev, value );
|
||||||
reg_vals[ 0 ] = 0;
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
reg_vals[ 14 ] = 1;
|
|
||||||
|
|
||||||
if ( reg_vals.count( clip_gain ) ) {
|
|
||||||
int value = reg_vals[ clip_gain ];
|
|
||||||
#if 0
|
|
||||||
std::cerr << "amp gain: " << gain
|
|
||||||
<< " clip_gain: " << clip_gain
|
|
||||||
<< " value: " << value
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
if ( hackrf_set_amp_enable( _dev, value ) == HACKRF_SUCCESS )
|
|
||||||
_amp_gain = clip_gain;
|
_amp_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,65 +691,18 @@ double hackrf_sink_c::get_gain( const std::string & name, size_t chan )
|
||||||
|
|
||||||
double hackrf_sink_c::set_if_gain( double gain, size_t chan )
|
double hackrf_sink_c::set_if_gain( double gain, size_t chan )
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
osmosdr::gain_range_t if_gains = get_gain_range( "IF", chan );
|
osmosdr::gain_range_t if_gains = get_gain_range( "IF", chan );
|
||||||
|
|
||||||
double clip_gain = if_gains.clip( gain, true );
|
|
||||||
double rel_atten = fabs( if_gains.stop() - clip_gain );
|
|
||||||
|
|
||||||
std::vector< osmosdr::gain_range_t > if_attens;
|
|
||||||
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 1, 1); /* chapter 1.5: TX Gain Control */
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 2, 2);
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 4, 4);
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 8, 8);
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 16, 16);
|
|
||||||
if_attens += osmosdr::gain_range_t(0, 16, 16);
|
|
||||||
|
|
||||||
std::map< int, double > attens;
|
|
||||||
|
|
||||||
/* initialize with min attens */
|
|
||||||
for (unsigned int i = 0; i < if_attens.size(); i++) {
|
|
||||||
attens[ i + 1 ] = if_attens[ i ].start();
|
|
||||||
}
|
|
||||||
|
|
||||||
double atten = rel_atten;
|
|
||||||
|
|
||||||
for (int i = if_attens.size() - 1; i >= 0; i--) {
|
|
||||||
osmosdr::gain_range_t range = if_attens[ i ];
|
|
||||||
|
|
||||||
if ( atten - range.stop() >= 0 ) {
|
|
||||||
atten -= range.stop();
|
|
||||||
attens[ i + 1 ] = range.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
std::cerr << rel_atten << " => "; double sum = 0;
|
|
||||||
for (unsigned int i = 0; i < attens.size(); i++) {
|
|
||||||
sum += attens[ i + 1 ];
|
|
||||||
std::cerr << attens[ i + 1 ] << " ";
|
|
||||||
}
|
|
||||||
std::cerr << " = " << sum << std::endl;
|
|
||||||
#endif
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
int value = 0;
|
double clip_gain = if_gains.clip( gain, true );
|
||||||
for (unsigned int stage = 1; stage <= attens.size(); stage++) {
|
|
||||||
if ( attens[ stage ] != 0 )
|
|
||||||
value |= 1 << (stage - 1);
|
|
||||||
}
|
|
||||||
#if 0
|
|
||||||
std::cerr << "vga gain: " << gain
|
|
||||||
<< " clip_gain: " << clip_gain
|
|
||||||
<< " rel_atten: " << rel_atten
|
|
||||||
<< " value: " << value
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
uint16_t val;
|
|
||||||
hackrf_max2837_read( _dev, 29, &val );
|
|
||||||
|
|
||||||
val = (val & 0xf) | ((value & 0x3f) << 4);
|
ret = hackrf_set_txvga_gain( _dev, uint32_t(clip_gain) );
|
||||||
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
if ( hackrf_max2837_write( _dev, 29, val ) == HACKRF_SUCCESS )
|
|
||||||
_vga_gain = clip_gain;
|
_vga_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_txvga_gain", clip_gain ) )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _vga_gain;
|
return _vga_gain;
|
||||||
|
@ -740,16 +738,16 @@ double hackrf_sink_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan );
|
// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan );
|
||||||
|
|
||||||
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
||||||
bandwidth = _sample_rate;
|
bandwidth = _sample_rate * 0.75; /* select narrower filters to prevent aliasing */
|
||||||
|
|
||||||
if ( _dev ) {
|
if ( _dev ) {
|
||||||
/* compute best default value depending on sample rate (auto filter) */
|
/* compute best default value depending on sample rate (auto filter) */
|
||||||
uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) );
|
uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) );
|
||||||
ret = hackrf_baseband_filter_bandwidth_set( _dev, bw );
|
ret = hackrf_set_baseband_filter_bandwidth( _dev, bw );
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_bandwidth = bw;
|
_bandwidth = bw;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ private:
|
||||||
// gruel::thread _thread;
|
// gruel::thread _thread;
|
||||||
|
|
||||||
circular_buffer_t _cbuf;
|
circular_buffer_t _cbuf;
|
||||||
unsigned char *_buf;
|
char *_buf;
|
||||||
unsigned int _buf_num;
|
unsigned int _buf_num;
|
||||||
unsigned int _buf_used;
|
unsigned int _buf_used;
|
||||||
boost::mutex _buf_mutex;
|
boost::mutex _buf_mutex;
|
||||||
|
|
|
@ -27,8 +27,8 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "hackrf_source_c.h"
|
#include <stdexcept>
|
||||||
#include <gnuradio/gr_io_signature.h>
|
#include <iostream>
|
||||||
|
|
||||||
#include <boost/assign.hpp>
|
#include <boost/assign.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
|
@ -36,18 +36,27 @@
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/thread/thread.hpp>
|
#include <boost/thread/thread.hpp>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <gnuradio/gr_io_signature.h>
|
||||||
#include <iostream>
|
|
||||||
|
#include "hackrf_source_c.h"
|
||||||
|
|
||||||
#include <osmosdr_arg_helpers.h>
|
#include <osmosdr_arg_helpers.h>
|
||||||
|
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
||||||
#define BUF_NUM 32
|
#define BUF_NUM 15
|
||||||
|
|
||||||
#define BYTES_PER_SAMPLE 2 /* HackRF device produces 8 bit unsigned IQ data */
|
#define BYTES_PER_SAMPLE 2 /* HackRF device produces 8 bit unsigned IQ data */
|
||||||
|
|
||||||
|
#define HACKRF_THROW_ON_ERROR(ret, msg) \
|
||||||
|
if ( ret != HACKRF_SUCCESS ) \
|
||||||
|
throw std::runtime_error( boost::str( boost::format(msg " (%d) %s") \
|
||||||
|
% ret % hackrf_error_name((enum hackrf_error)ret) ) );
|
||||||
|
|
||||||
|
#define HACKRF_FUNC_STR(func, arg) \
|
||||||
|
boost::str(boost::format(func "(%d)") % arg) + " has failed"
|
||||||
|
|
||||||
int hackrf_source_c::_usage = 0;
|
int hackrf_source_c::_usage = 0;
|
||||||
boost::mutex hackrf_source_c::_usage_mutex;
|
boost::mutex hackrf_source_c::_usage_mutex;
|
||||||
|
|
||||||
|
@ -85,10 +94,10 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
|
||||||
_auto_gain(false),
|
_auto_gain(false),
|
||||||
_amp_gain(0),
|
_amp_gain(0),
|
||||||
_lna_gain(0),
|
_lna_gain(0),
|
||||||
_vga_gain(0)
|
_vga_gain(0),
|
||||||
|
_bandwidth(0)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
uint16_t val;
|
|
||||||
|
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
|
@ -111,11 +120,11 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
|
||||||
// create a lookup table for gr_complex values
|
// create a lookup table for gr_complex values
|
||||||
for (unsigned int i = 0; i <= 0xffff; i++) {
|
for (unsigned int i = 0; i <= 0xffff; i++) {
|
||||||
#ifdef BOOST_LITTLE_ENDIAN
|
#ifdef BOOST_LITTLE_ENDIAN
|
||||||
_lut.push_back( gr_complex( (float(i & 0xff) - 127.5f) * (1.0f/128.0f),
|
_lut.push_back( gr_complex( (float(char(i & 0xff))) * (1.0f/128.0f),
|
||||||
(float(i >> 8) - 127.5f) * (1.0f/128.0f) ) );
|
(float(char(i >> 8))) * (1.0f/128.0f) ) );
|
||||||
#else // BOOST_BIG_ENDIAN
|
#else // BOOST_BIG_ENDIAN
|
||||||
_lut.push_back( gr_complex( (float(i >> 8) - 127.5f) * (1.0f/128.0f),
|
_lut.push_back( gr_complex( (float(char(i >> 8))) * (1.0f/128.0f),
|
||||||
(float(i & 0xff) - 127.5f) * (1.0f/128.0f) ) );
|
(float(char(i & 0xff))) * (1.0f/128.0f) ) );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,24 +139,21 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
|
||||||
|
|
||||||
_dev = NULL;
|
_dev = NULL;
|
||||||
ret = hackrf_open( &_dev );
|
ret = hackrf_open( &_dev );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device")
|
||||||
throw std::runtime_error("Failed to open HackRF device.");
|
|
||||||
|
|
||||||
uint8_t board_id;
|
uint8_t board_id;
|
||||||
ret = hackrf_board_id_read( _dev, &board_id );
|
ret = hackrf_board_id_read( _dev, &board_id );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id")
|
||||||
throw std::runtime_error("Failed to get board id.");
|
|
||||||
|
|
||||||
char version[40];
|
char version[40];
|
||||||
memset(version, 0, sizeof(version));
|
memset(version, 0, sizeof(version));
|
||||||
ret = hackrf_version_string_read( _dev, version, sizeof(version));
|
ret = hackrf_version_string_read( _dev, version, sizeof(version));
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to read version string")
|
||||||
throw std::runtime_error("Failed to read version string.");
|
|
||||||
#if 0
|
#if 0
|
||||||
read_partid_serialno_t serial_number;
|
read_partid_serialno_t serial_number;
|
||||||
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
|
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
|
||||||
if (ret != HACKRF_SUCCESS)
|
HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number")
|
||||||
throw std::runtime_error("Failed to read serial number.");
|
|
||||||
#endif
|
#endif
|
||||||
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
|
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
|
||||||
<< "with firmware " << version << " "
|
<< "with firmware " << version << " "
|
||||||
|
@ -158,13 +164,11 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_sample_rate( 5000000 );
|
set_center_freq( (get_freq_range().start() + get_freq_range().stop()) / 2.0 );
|
||||||
|
set_sample_rate( get_sample_rates().start() );
|
||||||
|
set_bandwidth( 0 );
|
||||||
|
|
||||||
set_gain( 0 ); /* disable AMP gain stage */
|
set_gain( 14 ); /* enable AMP gain stage by default */
|
||||||
|
|
||||||
hackrf_max2837_read( _dev, 8, &val );
|
|
||||||
val |= 0x3; /* enable LNA & VGA control over SPI */
|
|
||||||
hackrf_max2837_write( _dev, 8, val );
|
|
||||||
|
|
||||||
set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */
|
set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */
|
||||||
|
|
||||||
|
@ -178,6 +182,9 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
|
||||||
}
|
}
|
||||||
|
|
||||||
// _thread = gruel::thread(_hackrf_wait, this);
|
// _thread = gruel::thread(_hackrf_wait, this);
|
||||||
|
|
||||||
|
ret = hackrf_start_rx( _dev, _hackrf_rx_callback, (void *)this );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to start RX streaming")
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -187,7 +194,10 @@ hackrf_source_c::~hackrf_source_c ()
|
||||||
{
|
{
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
// _thread.join();
|
// _thread.join();
|
||||||
hackrf_close( _dev );
|
int ret = hackrf_stop_rx( _dev );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to stop RX streaming")
|
||||||
|
ret = hackrf_close( _dev );
|
||||||
|
HACKRF_THROW_ON_ERROR(ret, "Failed to close HackRF")
|
||||||
_dev = NULL;
|
_dev = NULL;
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -251,13 +261,13 @@ bool hackrf_source_c::start()
|
||||||
{
|
{
|
||||||
if ( ! _dev )
|
if ( ! _dev )
|
||||||
return false;
|
return false;
|
||||||
|
#if 0
|
||||||
int ret = hackrf_start_rx( _dev, _hackrf_rx_callback, (void *)this );
|
int ret = hackrf_start_rx( _dev, _hackrf_rx_callback, (void *)this );
|
||||||
if ( ret != HACKRF_SUCCESS ) {
|
if ( ret != HACKRF_SUCCESS ) {
|
||||||
std::cerr << "Failed to start RX streaming (" << ret << ")" << std::endl;
|
std::cerr << "Failed to start RX streaming (" << ret << ")" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,13 +275,13 @@ bool hackrf_source_c::stop()
|
||||||
{
|
{
|
||||||
if ( ! _dev )
|
if ( ! _dev )
|
||||||
return false;
|
return false;
|
||||||
|
#if 0
|
||||||
int ret = hackrf_stop_rx( _dev );
|
int ret = hackrf_stop_rx( _dev );
|
||||||
if ( ret != HACKRF_SUCCESS ) {
|
if ( ret != HACKRF_SUCCESS ) {
|
||||||
std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
|
std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,7 +343,7 @@ std::vector<std::string> hackrf_source_c::get_devices()
|
||||||
{
|
{
|
||||||
std::vector<std::string> devices;
|
std::vector<std::string> devices;
|
||||||
std::string label;
|
std::string label;
|
||||||
|
#if 0
|
||||||
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; i++) {
|
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; i++) {
|
||||||
std::string args = "hackrf=" + boost::lexical_cast< std::string >( i );
|
std::string args = "hackrf=" + boost::lexical_cast< std::string >( i );
|
||||||
|
|
||||||
|
@ -346,7 +356,49 @@ std::vector<std::string> hackrf_source_c::get_devices()
|
||||||
args += ",label='" + label + "'";
|
args += ",label='" + label + "'";
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
hackrf_init(); /* call only once before the first open */
|
||||||
|
|
||||||
|
_usage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
hackrf_device *dev = NULL;
|
||||||
|
ret = hackrf_open(&dev);
|
||||||
|
if ( HACKRF_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
std::string args = "hackrf=0";
|
||||||
|
|
||||||
|
label = "HackRF";
|
||||||
|
|
||||||
|
uint8_t board_id;
|
||||||
|
ret = hackrf_board_id_read( dev, &board_id );
|
||||||
|
if ( HACKRF_SUCCESS == ret )
|
||||||
|
{
|
||||||
|
label += std::string(" ") + hackrf_board_id_name(hackrf_board_id(board_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
args += ",label='" + label + "'";
|
||||||
|
devices.push_back( args );
|
||||||
|
|
||||||
|
ret = hackrf_close(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||||
|
|
||||||
|
_usage--;
|
||||||
|
|
||||||
|
if ( _usage == 0 )
|
||||||
|
hackrf_exit(); /* call only once after last close */
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +411,11 @@ osmosdr::meta_range_t hackrf_source_c::get_sample_rates()
|
||||||
{
|
{
|
||||||
osmosdr::meta_range_t range;
|
osmosdr::meta_range_t range;
|
||||||
|
|
||||||
range += osmosdr::range_t( 5e6 ); /* out of spec but appears to work */
|
/* we only add integer rates here because of better phase noise performance.
|
||||||
|
* the user is allowed to request arbitrary (fractional) rates within these
|
||||||
|
* boundaries. */
|
||||||
|
|
||||||
|
range += osmosdr::range_t( 8e6 );
|
||||||
range += osmosdr::range_t( 10e6 );
|
range += osmosdr::range_t( 10e6 );
|
||||||
range += osmosdr::range_t( 12.5e6 );
|
range += osmosdr::range_t( 12.5e6 );
|
||||||
range += osmosdr::range_t( 16e6 );
|
range += osmosdr::range_t( 16e6 );
|
||||||
|
@ -373,12 +429,12 @@ double hackrf_source_c::set_sample_rate(double rate)
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
ret = hackrf_sample_rate_set( _dev, uint32_t(rate) );
|
ret = hackrf_set_sample_rate( _dev, rate );
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_sample_rate = rate;
|
_sample_rate = rate;
|
||||||
set_bandwidth( rate );
|
//set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_sample_rate", rate ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,7 +467,7 @@ double hackrf_source_c::set_center_freq( double freq, size_t chan )
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_center_freq = freq;
|
_center_freq = freq;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_freq", corr_freq ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,25 +540,18 @@ bool hackrf_source_c::get_gain_mode( size_t chan )
|
||||||
|
|
||||||
double hackrf_source_c::set_gain( double gain, size_t chan )
|
double hackrf_source_c::set_gain( double gain, size_t chan )
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
|
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
double clip_gain = rf_gains.clip( gain, true );
|
double clip_gain = rf_gains.clip( gain, true );
|
||||||
|
uint8_t value = clip_gain == 14.0f ? 1 : 0;
|
||||||
|
|
||||||
std::map<double, int> reg_vals;
|
ret = hackrf_set_amp_enable( _dev, value );
|
||||||
reg_vals[ 0 ] = 0;
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
reg_vals[ 14 ] = 1;
|
|
||||||
|
|
||||||
if ( reg_vals.count( clip_gain ) ) {
|
|
||||||
int value = reg_vals[ clip_gain ];
|
|
||||||
#if 0
|
|
||||||
std::cerr << "amp gain: " << gain
|
|
||||||
<< " clip_gain: " << clip_gain
|
|
||||||
<< " value: " << value
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
if ( hackrf_set_amp_enable( _dev, value ) == HACKRF_SUCCESS )
|
|
||||||
_amp_gain = clip_gain;
|
_amp_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,36 +599,17 @@ double hackrf_source_c::get_gain( const std::string & name, size_t chan )
|
||||||
|
|
||||||
double hackrf_source_c::set_if_gain(double gain, size_t chan)
|
double hackrf_source_c::set_if_gain(double gain, size_t chan)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
osmosdr::gain_range_t rf_gains = get_gain_range( "IF", chan );
|
osmosdr::gain_range_t rf_gains = get_gain_range( "IF", chan );
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
double clip_gain = rf_gains.clip( gain, true );
|
double clip_gain = rf_gains.clip( gain, true );
|
||||||
double rel_gain = fabs( rf_gains.stop() - clip_gain );
|
|
||||||
|
|
||||||
std::map<double, int> reg_vals;
|
ret = hackrf_set_lna_gain( _dev, uint32_t(clip_gain) );
|
||||||
reg_vals[ 0 ] = 0;
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
reg_vals[ 8 ] = 4;
|
|
||||||
reg_vals[ 16 ] = 2;
|
|
||||||
reg_vals[ 24 ] = 6;
|
|
||||||
reg_vals[ 32 ] = 3;
|
|
||||||
reg_vals[ 40 ] = 7;
|
|
||||||
|
|
||||||
if ( reg_vals.count( rel_gain ) ) {
|
|
||||||
int value = reg_vals[ rel_gain ];
|
|
||||||
#if 0
|
|
||||||
std::cerr << "lna gain: " << gain
|
|
||||||
<< " clip_gain: " << clip_gain
|
|
||||||
<< " rel_gain: " << rel_gain
|
|
||||||
<< " value: " << value
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
uint16_t val;
|
|
||||||
hackrf_max2837_read( _dev, 1, &val );
|
|
||||||
|
|
||||||
val = (val & ~(7 << 2)) | ((value & 7) << 2);
|
|
||||||
|
|
||||||
if ( hackrf_max2837_write( _dev, 1, val ) == HACKRF_SUCCESS )
|
|
||||||
_lna_gain = clip_gain;
|
_lna_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_lna_gain", clip_gain ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -588,33 +618,17 @@ double hackrf_source_c::set_if_gain(double gain, size_t chan)
|
||||||
|
|
||||||
double hackrf_source_c::set_bb_gain( double gain, size_t chan )
|
double hackrf_source_c::set_bb_gain( double gain, size_t chan )
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
osmosdr::gain_range_t if_gains = get_gain_range( "BB", chan );
|
osmosdr::gain_range_t if_gains = get_gain_range( "BB", chan );
|
||||||
|
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
double clip_gain = if_gains.clip( gain, true );
|
double clip_gain = if_gains.clip( gain, true );
|
||||||
double rel_gain = fabs( if_gains.stop() - clip_gain );
|
|
||||||
|
|
||||||
std::map<double, int> reg_vals;
|
ret = hackrf_set_vga_gain( _dev, uint32_t(clip_gain) );
|
||||||
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
for ( int i = 0; i < 31; i++ )
|
|
||||||
reg_vals[ 2.0 * i ] = i;
|
|
||||||
|
|
||||||
if ( reg_vals.count( rel_gain ) ) {
|
|
||||||
int value = reg_vals[ rel_gain ];
|
|
||||||
#if 0
|
|
||||||
std::cerr << "vga gain: " << gain
|
|
||||||
<< " clip_gain: " << clip_gain
|
|
||||||
<< " rel_gain: " << rel_gain
|
|
||||||
<< " value: " << value
|
|
||||||
<< std::endl;
|
|
||||||
#endif
|
|
||||||
uint16_t val;
|
|
||||||
hackrf_max2837_read( _dev, 5, &val );
|
|
||||||
|
|
||||||
val = (val & ~0x1f) | (value & 0x1f);
|
|
||||||
|
|
||||||
if ( hackrf_max2837_write( _dev, 5, val ) == HACKRF_SUCCESS )
|
|
||||||
_vga_gain = clip_gain;
|
_vga_gain = clip_gain;
|
||||||
|
} else {
|
||||||
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_vga_gain", clip_gain ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -646,16 +660,16 @@ double hackrf_source_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan );
|
// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan );
|
||||||
|
|
||||||
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
||||||
bandwidth = _sample_rate;
|
bandwidth = _sample_rate * 0.75; /* select narrower filters to prevent aliasing */
|
||||||
|
|
||||||
if ( _dev ) {
|
if ( _dev ) {
|
||||||
/* compute best default value depending on sample rate (auto filter) */
|
/* compute best default value depending on sample rate (auto filter) */
|
||||||
uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) );
|
uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) );
|
||||||
ret = hackrf_baseband_filter_bandwidth_set( _dev, bw );
|
ret = hackrf_set_baseband_filter_bandwidth( _dev, bw );
|
||||||
if ( HACKRF_SUCCESS == ret ) {
|
if ( HACKRF_SUCCESS == ret ) {
|
||||||
_bandwidth = bw;
|
_bandwidth = bw;
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${LIBMIRISDR_INCLUDE_DIR}
|
${LIBMIRISDR_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(mirisdr_srcs
|
set(mirisdr_srcs
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
#define BUF_SIZE 2304 * 8 * 2
|
#define BUF_SIZE 2304 * 8 * 2
|
||||||
#define BUF_NUM 32
|
#define BUF_NUM 15
|
||||||
#define BUF_SKIP 1 // buffers to skip due to garbage
|
#define BUF_SKIP 1 // buffers to skip due to garbage
|
||||||
|
|
||||||
#define BYTES_PER_SAMPLE 4 // mirisdr device delivers 16 bit signed IQ data
|
#define BYTES_PER_SAMPLE 4 // mirisdr device delivers 16 bit signed IQ data
|
||||||
|
@ -210,7 +210,7 @@ void miri_source_c::_mirisdr_wait(miri_source_c *obj)
|
||||||
|
|
||||||
void miri_source_c::mirisdr_wait()
|
void miri_source_c::mirisdr_wait()
|
||||||
{
|
{
|
||||||
int ret = mirisdr_read_async( _dev, _mirisdr_callback, (void *)this, 0, BUF_SIZE );
|
int ret = mirisdr_read_async( _dev, _mirisdr_callback, (void *)this, _buf_num, BUF_SIZE );
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${LIBOSMOSDR_INCLUDE_DIR}
|
${LIBOSMOSDR_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(osmosdr_srcs
|
set(osmosdr_srcs
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
||||||
#define BUF_NUM 32
|
#define BUF_NUM 15
|
||||||
#define BUF_SKIP 1 // buffers to skip due to garbage
|
#define BUF_SKIP 1 // buffers to skip due to garbage
|
||||||
|
|
||||||
#define BYTES_PER_SAMPLE 4 // osmosdr device delivers 16 bit signed IQ data
|
#define BYTES_PER_SAMPLE 4 // osmosdr device delivers 16 bit signed IQ data
|
||||||
|
@ -203,7 +203,7 @@ void osmosdr_src_c::_osmosdr_wait(osmosdr_src_c *obj)
|
||||||
|
|
||||||
void osmosdr_src_c::osmosdr_wait()
|
void osmosdr_src_c::osmosdr_wait()
|
||||||
{
|
{
|
||||||
int ret = osmosdr_read_async( _dev, _osmosdr_callback, (void *)this, 0, _buf_len );
|
int ret = osmosdr_read_async( _dev, _osmosdr_callback, (void *)this, _buf_num, _buf_len );
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@ struct is_nchan_argument
|
||||||
{
|
{
|
||||||
bool operator ()(const std::string &str)
|
bool operator ()(const std::string &str)
|
||||||
{
|
{
|
||||||
return str.find("nchan=") == 0;
|
return str.find("numchan=") == 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,7 +114,7 @@ inline gr_io_signature_sptr args_to_io_signature( const std::string &args )
|
||||||
|
|
||||||
BOOST_FOREACH( std::string arg, arg_list )
|
BOOST_FOREACH( std::string arg, arg_list )
|
||||||
{
|
{
|
||||||
if ( arg.find( "nchan=" ) == 0 ) // try to parse global nchan value
|
if ( arg.find( "numchan=" ) == 0 ) // try to parse global nchan value
|
||||||
{
|
{
|
||||||
pair_t pair = param_to_pair( arg );
|
pair_t pair = param_to_pair( arg );
|
||||||
max_nchan = boost::lexical_cast<size_t>( pair.second );
|
max_nchan = boost::lexical_cast<size_t>( pair.second );
|
||||||
|
|
|
@ -62,6 +62,18 @@
|
||||||
#include <hackrf_source_c.h>
|
#include <hackrf_source_c.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
#include <bladerf_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
#include <rfspace_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
#include <airspy_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "osmosdr_arg_helpers.h"
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
|
||||||
using namespace osmosdr;
|
using namespace osmosdr;
|
||||||
|
@ -111,6 +123,11 @@ devices_t device::find(const device_t &hint)
|
||||||
{
|
{
|
||||||
boost::mutex::scoped_lock lock(_device_mutex);
|
boost::mutex::scoped_lock lock(_device_mutex);
|
||||||
|
|
||||||
|
bool fake = true;
|
||||||
|
|
||||||
|
if ( hint.count("nofake") )
|
||||||
|
fake = false;
|
||||||
|
|
||||||
devices_t devices;
|
devices_t devices;
|
||||||
|
|
||||||
#ifdef ENABLE_OSMOSDR
|
#ifdef ENABLE_OSMOSDR
|
||||||
|
@ -133,21 +150,32 @@ devices_t device::find(const device_t &hint)
|
||||||
BOOST_FOREACH( std::string dev, miri_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, miri_source_c::get_devices() )
|
||||||
devices.push_back( device_t(dev) );
|
devices.push_back( device_t(dev) );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() )
|
||||||
|
devices.push_back( device_t(dev) );
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
|
||||||
devices.push_back( device_t(dev) );
|
devices.push_back( device_t(dev) );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
BOOST_FOREACH( std::string dev, rfspace_source_c::get_devices( fake ) )
|
||||||
|
devices.push_back( device_t(dev) );
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
|
||||||
|
devices.push_back( device_t(dev) );
|
||||||
|
#endif
|
||||||
/* software-only sources should be appended at the very end,
|
/* software-only sources should be appended at the very end,
|
||||||
* hopefully resulting in hardware sources to be shown first
|
* hopefully resulting in hardware sources to be shown first
|
||||||
* in a graphical interface etc... */
|
* in a graphical interface etc... */
|
||||||
|
|
||||||
#ifdef ENABLE_RTL_TCP
|
#ifdef ENABLE_RTL_TCP
|
||||||
BOOST_FOREACH( std::string dev, rtl_tcp_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, rtl_tcp_source_c::get_devices( fake ) )
|
||||||
devices.push_back( device_t(dev) );
|
devices.push_back( device_t(dev) );
|
||||||
#endif
|
#endif
|
||||||
#ifdef ENABLE_FILE
|
#ifdef ENABLE_FILE
|
||||||
BOOST_FOREACH( std::string dev, file_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, file_source_c::get_devices( fake ) )
|
||||||
devices.push_back( device_t(dev) );
|
devices.push_back( device_t(dev) );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,9 @@
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
#include "hackrf_sink_c.h"
|
#include "hackrf_sink_c.h"
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
#include "bladerf_sink_c.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "osmosdr_arg_helpers.h"
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
|
||||||
|
@ -63,7 +66,8 @@ osmosdr_make_sink_c (const std::string &args)
|
||||||
osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||||
: gr_hier_block2 ("osmosdr_sink_c_impl",
|
: gr_hier_block2 ("osmosdr_sink_c_impl",
|
||||||
args_to_io_signature(args),
|
args_to_io_signature(args),
|
||||||
gr_make_io_signature (0, 0, 0))
|
gr_make_io_signature (0, 0, 0)),
|
||||||
|
_sample_rate(NAN)
|
||||||
{
|
{
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
bool device_specified = false;
|
bool device_specified = false;
|
||||||
|
@ -78,10 +82,13 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
dev_types.push_back("hackrf");
|
dev_types.push_back("hackrf");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
dev_types.push_back("bladerf");
|
||||||
|
#endif
|
||||||
std::cerr << "gr-osmosdr "
|
std::cerr << "gr-osmosdr "
|
||||||
<< GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") "
|
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
|
||||||
<< "gnuradio " << gr_version() << std::endl;
|
<< "gnuradio " << gr_version() << std::endl;
|
||||||
|
|
||||||
std::cerr << "built-in sink types: ";
|
std::cerr << "built-in sink types: ";
|
||||||
BOOST_FOREACH(std::string dev_type, dev_types)
|
BOOST_FOREACH(std::string dev_type, dev_types)
|
||||||
std::cerr << dev_type << " ";
|
std::cerr << dev_type << " ";
|
||||||
|
@ -104,10 +111,15 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||||
BOOST_FOREACH( std::string dev, uhd_sink_c::get_devices() )
|
BOOST_FOREACH( std::string dev, uhd_sink_c::get_devices() )
|
||||||
dev_list.push_back( dev );
|
dev_list.push_back( dev );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
BOOST_FOREACH( std::string dev, bladerf_sink_c::get_devices() )
|
||||||
|
dev_list.push_back( dev );
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() )
|
BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() )
|
||||||
dev_list.push_back( dev );
|
dev_list.push_back( dev );
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// std::cerr << std::endl;
|
// std::cerr << std::endl;
|
||||||
// BOOST_FOREACH( std::string dev, dev_list )
|
// BOOST_FOREACH( std::string dev, dev_list )
|
||||||
// std::cerr << "'" << dev << "'" << std::endl;
|
// std::cerr << "'" << dev << "'" << std::endl;
|
||||||
|
@ -142,6 +154,12 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||||
block = sink; iface = sink.get();
|
block = sink; iface = sink.get();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
if ( dict.count("bladerf") ) {
|
||||||
|
bladerf_sink_c_sptr sink = make_bladerf_sink_c( arg );
|
||||||
|
block = sink; iface = sink.get();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( iface != NULL && long(block.get()) != 0 ) {
|
if ( iface != NULL && long(block.get()) != 0 ) {
|
||||||
_devs.push_back( iface );
|
_devs.push_back( iface );
|
||||||
|
@ -160,12 +178,14 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||||
} catch ( std::exception &ex ) {
|
} catch ( std::exception &ex ) {
|
||||||
std::cerr << std::endl << "FATAL: " << ex.what() << std::endl << std::endl;
|
std::cerr << std::endl << "FATAL: " << ex.what() << std::endl << std::endl;
|
||||||
|
|
||||||
size_t missing_chans = output_signature()->max_streams() - channel;
|
size_t missing_chans = 0;
|
||||||
|
if ( input_signature()->max_streams() > 0 )
|
||||||
|
missing_chans = input_signature()->max_streams() - channel;
|
||||||
|
|
||||||
std::cerr << "Trying to fill up " << missing_chans
|
std::cerr << "Trying to fill up " << missing_chans
|
||||||
<< " missing channel(s) with null sinks.\n"
|
<< " missing channel(s) with null sinks.\n"
|
||||||
<< "This is being done to prevent the application from crashing\n"
|
<< "This is being done to prevent the application from crashing\n"
|
||||||
<< "due to a gnuradio bug. The maintainers have been informed.\n"
|
<< "due to gnuradio bug #528.\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
for (size_t i = 0; i < missing_chans; i++) {
|
for (size_t i = 0; i < missing_chans; i++) {
|
||||||
|
@ -252,10 +272,11 @@ double osmosdr_sink_c_impl::set_center_freq( double freq, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _center_freq[ chan ] != freq ) {
|
if ( _center_freq[ chan ] != freq ) {
|
||||||
_center_freq[ chan ] = freq;
|
_center_freq[ chan ] = freq;
|
||||||
return dev->set_center_freq( freq, dev_chan );
|
return dev->set_center_freq( freq, dev_chan );
|
||||||
|
} else { return _center_freq[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -277,10 +298,11 @@ double osmosdr_sink_c_impl::set_freq_corr( double ppm, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _freq_corr[ chan ] != ppm ) {
|
if ( _freq_corr[ chan ] != ppm ) {
|
||||||
_freq_corr[ chan ] = ppm;
|
_freq_corr[ chan ] = ppm;
|
||||||
return dev->set_freq_corr( ppm, dev_chan );
|
return dev->set_freq_corr( ppm, dev_chan );
|
||||||
|
} else { return _freq_corr[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -335,13 +357,14 @@ bool osmosdr_sink_c_impl::set_gain_mode( bool automatic, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _gain_mode[ chan ] != automatic ) {
|
if ( _gain_mode[ chan ] != automatic ) {
|
||||||
_gain_mode[ chan ] = automatic;
|
_gain_mode[ chan ] = automatic;
|
||||||
bool mode = dev->set_gain_mode( automatic, dev_chan );
|
bool mode = dev->set_gain_mode( automatic, dev_chan );
|
||||||
if (!automatic) // reapply gain value when switched to manual mode
|
if (!automatic) // reapply gain value when switched to manual mode
|
||||||
dev->set_gain( _gain[ chan ], dev_chan );
|
dev->set_gain( _gain[ chan ], dev_chan );
|
||||||
return mode;
|
return mode;
|
||||||
|
} else { return _gain_mode[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -363,10 +386,11 @@ double osmosdr_sink_c_impl::set_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _gain[ chan ] != gain ) {
|
if ( _gain[ chan ] != gain ) {
|
||||||
_gain[ chan ] = gain;
|
_gain[ chan ] = gain;
|
||||||
return dev->set_gain( gain, dev_chan );
|
return dev->set_gain( gain, dev_chan );
|
||||||
|
} else { return _gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -410,10 +434,11 @@ double osmosdr_sink_c_impl::set_if_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _if_gain[ chan ] != gain ) {
|
if ( _if_gain[ chan ] != gain ) {
|
||||||
_if_gain[ chan ] = gain;
|
_if_gain[ chan ] = gain;
|
||||||
return dev->set_if_gain( gain, dev_chan );
|
return dev->set_if_gain( gain, dev_chan );
|
||||||
|
} else { return _if_gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -424,10 +449,11 @@ double osmosdr_sink_c_impl::set_bb_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _bb_gain[ chan ] != gain ) {
|
if ( _bb_gain[ chan ] != gain ) {
|
||||||
_bb_gain[ chan ] = gain;
|
_bb_gain[ chan ] = gain;
|
||||||
return dev->set_bb_gain( gain, dev_chan );
|
return dev->set_bb_gain( gain, dev_chan );
|
||||||
|
} else { return _bb_gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -449,10 +475,11 @@ std::string osmosdr_sink_c_impl::set_antenna( const std::string & antenna, size_
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _antenna[ chan ] != antenna ) {
|
if ( _antenna[ chan ] != antenna ) {
|
||||||
_antenna[ chan ] = antenna;
|
_antenna[ chan ] = antenna;
|
||||||
return dev->set_antenna( antenna, dev_chan );
|
return dev->set_antenna( antenna, dev_chan );
|
||||||
|
} else { return _antenna[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -469,14 +496,22 @@ std::string osmosdr_sink_c_impl::get_antenna( size_t chan )
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
void osmosdr_sink_c_impl::set_iq_balance_mode( int mode, size_t chan )
|
void osmosdr_sink_c_impl::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
{
|
{
|
||||||
|
size_t channel = 0;
|
||||||
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
dev->set_dc_offset( offset, dev_chan );
|
||||||
}
|
}
|
||||||
|
|
||||||
void osmosdr_sink_c_impl::set_iq_balance( const std::complex<double> &correction, size_t chan )
|
void osmosdr_sink_c_impl::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
{
|
{
|
||||||
|
size_t channel = 0;
|
||||||
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
dev->set_iq_balance( balance, dev_chan );
|
||||||
}
|
}
|
||||||
|
|
||||||
double osmosdr_sink_c_impl::set_bandwidth( double bandwidth, size_t chan )
|
double osmosdr_sink_c_impl::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
@ -484,10 +519,11 @@ double osmosdr_sink_c_impl::set_bandwidth( double bandwidth, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _bandwidth[ chan ] != bandwidth ) {
|
if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
|
||||||
_bandwidth[ chan ] = bandwidth;
|
_bandwidth[ chan ] = bandwidth;
|
||||||
return dev->set_bandwidth( bandwidth, dev_chan );
|
return dev->set_bandwidth( bandwidth, dev_chan );
|
||||||
|
} else { return _bandwidth[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -58,8 +58,9 @@ public:
|
||||||
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
void set_iq_balance_mode( int mode, size_t chan = 0 );
|
void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 );
|
||||||
void set_iq_balance( const std::complex<double> &correction, size_t chan = 0 );
|
|
||||||
|
void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 );
|
||||||
|
|
||||||
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
double get_bandwidth( size_t chan = 0 );
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
@ -74,6 +75,7 @@ private:
|
||||||
|
|
||||||
std::vector< osmosdr_snk_iface * > _devs;
|
std::vector< osmosdr_snk_iface * > _devs;
|
||||||
|
|
||||||
|
/* cache to prevent multiple device calls with the same value coming from grc */
|
||||||
double _sample_rate;
|
double _sample_rate;
|
||||||
std::map< size_t, double > _center_freq;
|
std::map< size_t, double > _center_freq;
|
||||||
std::map< size_t, double > _freq_corr;
|
std::map< size_t, double > _freq_corr;
|
||||||
|
|
|
@ -218,25 +218,26 @@ public:
|
||||||
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the TX frontend IQ balance mode.
|
* Set the TX frontend DC offset value.
|
||||||
|
* The value is complex to control both I and Q.
|
||||||
*
|
*
|
||||||
* \param mode iq balance correction mode: 0 = Off, 1 = Manual, 2 = Automatic
|
* \param offset the dc offset (1.0 is full-scale)
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance_mode( int mode, size_t chan = 0 ) { }
|
virtual void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 ) { }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the TX frontend IQ balance correction.
|
* Set the TX frontend IQ balance correction.
|
||||||
* Use this to adjust the magnitude and phase of I and Q.
|
* Use this to adjust the magnitude and phase of I and Q.
|
||||||
*
|
*
|
||||||
* \param correction the complex correction value
|
* \param balance the complex correction value
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance( const std::complex<double> &correction, size_t chan = 0 ) { }
|
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) { }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the bandpass filter on the radio frontend.
|
* Set the bandpass filter on the radio frontend.
|
||||||
* \param bandwidth the filter bandwidth in Hz
|
* \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
* \return the actual filter bandwidth in Hz
|
* \return the actual filter bandwidth in Hz
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -65,6 +65,18 @@
|
||||||
#include <hackrf_source_c.h>
|
#include <hackrf_source_c.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
#include <bladerf_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
#include <rfspace_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
#include <airspy_source_c.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <osmosdr_arg_helpers.h>
|
#include <osmosdr_arg_helpers.h>
|
||||||
|
|
||||||
/* This avoids throws in ctor of gr_hier_block2, as gnuradio is unable to deal
|
/* This avoids throws in ctor of gr_hier_block2, as gnuradio is unable to deal
|
||||||
|
@ -87,7 +99,8 @@ osmosdr_make_source_c (const std::string &args)
|
||||||
osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
||||||
: gr_hier_block2 ("osmosdr_source_c_impl",
|
: gr_hier_block2 ("osmosdr_source_c_impl",
|
||||||
gr_make_io_signature (0, 0, 0),
|
gr_make_io_signature (0, 0, 0),
|
||||||
args_to_io_signature(args))
|
args_to_io_signature(args)),
|
||||||
|
_sample_rate(NAN)
|
||||||
{
|
{
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
bool device_specified = false;
|
bool device_specified = false;
|
||||||
|
@ -120,15 +133,30 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
dev_types.push_back("hackrf");
|
dev_types.push_back("hackrf");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
dev_types.push_back("bladerf");
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
dev_types.push_back("rfspace");
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
dev_types.push_back("airspy");
|
||||||
|
#endif
|
||||||
std::cerr << "gr-osmosdr "
|
std::cerr << "gr-osmosdr "
|
||||||
<< GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") "
|
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
|
||||||
<< "gnuradio " << gr_version() << std::endl;
|
<< "gnuradio " << gr_version() << std::endl;
|
||||||
|
|
||||||
std::cerr << "built-in source types: ";
|
std::cerr << "built-in source types: ";
|
||||||
BOOST_FOREACH(std::string dev_type, dev_types)
|
BOOST_FOREACH(std::string dev_type, dev_types)
|
||||||
std::cerr << dev_type << " ";
|
std::cerr << dev_type << " ";
|
||||||
std::cerr << std::endl << std::flush;
|
std::cerr << std::endl << std::flush;
|
||||||
|
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
dev_types.push_back("sdr-iq"); /* additional aliases for rfspace backend */
|
||||||
|
dev_types.push_back("sdr-ip");
|
||||||
|
dev_types.push_back("netsdr");
|
||||||
|
#endif
|
||||||
|
|
||||||
BOOST_FOREACH(std::string arg, arg_list) {
|
BOOST_FOREACH(std::string arg, arg_list) {
|
||||||
dict_t dict = params_to_dict(arg);
|
dict_t dict = params_to_dict(arg);
|
||||||
BOOST_FOREACH(std::string dev_type, dev_types) {
|
BOOST_FOREACH(std::string dev_type, dev_types) {
|
||||||
|
@ -162,10 +190,23 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
||||||
BOOST_FOREACH( std::string dev, miri_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, miri_source_c::get_devices() )
|
||||||
dev_list.push_back( dev );
|
dev_list.push_back( dev );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() )
|
||||||
|
dev_list.push_back( dev );
|
||||||
|
#endif
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
BOOST_FOREACH( std::string dev, rfspace_source_c::get_devices() )
|
||||||
|
dev_list.push_back( dev );
|
||||||
|
#endif
|
||||||
#ifdef ENABLE_HACKRF
|
#ifdef ENABLE_HACKRF
|
||||||
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
|
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
|
||||||
dev_list.push_back( dev );
|
dev_list.push_back( dev );
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
|
||||||
|
dev_list.push_back( dev );
|
||||||
|
#endif
|
||||||
|
|
||||||
// std::cerr << std::endl;
|
// std::cerr << std::endl;
|
||||||
// BOOST_FOREACH( std::string dev, dev_list )
|
// BOOST_FOREACH( std::string dev, dev_list )
|
||||||
// std::cerr << "'" << dev << "'" << std::endl;
|
// std::cerr << "'" << dev << "'" << std::endl;
|
||||||
|
@ -244,6 +285,30 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_BLADERF
|
||||||
|
if ( dict.count("bladerf") ) {
|
||||||
|
bladerf_source_c_sptr src = make_bladerf_source_c( arg );
|
||||||
|
block = src; iface = src.get();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_RFSPACE
|
||||||
|
if ( dict.count("rfspace") ||
|
||||||
|
dict.count("sdr-iq") ||
|
||||||
|
dict.count("sdr-ip") ||
|
||||||
|
dict.count("netsdr") ) {
|
||||||
|
rfspace_source_c_sptr src = make_rfspace_source_c( arg );
|
||||||
|
block = src; iface = src.get();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_AIRSPY
|
||||||
|
if ( dict.count("airspy") ) {
|
||||||
|
airspy_source_c_sptr src = make_airspy_source_c( arg );
|
||||||
|
block = src; iface = src.get();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if ( iface != NULL && long(block.get()) != 0 ) {
|
if ( iface != NULL && long(block.get()) != 0 ) {
|
||||||
_devs.push_back( iface );
|
_devs.push_back( iface );
|
||||||
|
|
||||||
|
@ -284,12 +349,14 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
|
||||||
|
|
||||||
connect(noise_source, 0, throttle, 0);
|
connect(noise_source, 0, throttle, 0);
|
||||||
|
|
||||||
size_t missing_chans = output_signature()->max_streams() - channel;
|
size_t missing_chans = 0;
|
||||||
|
if ( output_signature()->max_streams() > 0 )
|
||||||
|
missing_chans = output_signature()->max_streams() - channel;
|
||||||
|
|
||||||
std::cerr << "Trying to fill up " << missing_chans
|
std::cerr << "Trying to fill up " << missing_chans
|
||||||
<< " missing channel(s) with gaussian noise.\n"
|
<< " missing channel(s) with gaussian noise.\n"
|
||||||
<< "This is being done to prevent the application from crashing\n"
|
<< "This is being done to prevent the application from crashing\n"
|
||||||
<< "due to a gnuradio bug. The maintainers have been informed.\n"
|
<< "due to gnuradio bug #528.\n"
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
|
|
||||||
for (size_t i = 0; i < missing_chans; i++)
|
for (size_t i = 0; i < missing_chans; i++)
|
||||||
|
@ -308,6 +375,17 @@ size_t osmosdr_source_c_impl::get_num_channels()
|
||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool osmosdr_source_c_impl::seek( long seek_point, int whence, size_t chan )
|
||||||
|
{
|
||||||
|
size_t channel = 0;
|
||||||
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
return dev->seek( seek_point, whence, dev_chan );
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#define NO_DEVICES_MSG "FATAL: No device(s) available to work with."
|
#define NO_DEVICES_MSG "FATAL: No device(s) available to work with."
|
||||||
|
|
||||||
osmosdr::meta_range_t osmosdr_source_c_impl::get_sample_rates()
|
osmosdr::meta_range_t osmosdr_source_c_impl::get_sample_rates()
|
||||||
|
@ -386,10 +464,11 @@ double osmosdr_source_c_impl::set_center_freq( double freq, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _center_freq[ chan ] != freq ) {
|
if ( _center_freq[ chan ] != freq ) {
|
||||||
_center_freq[ chan ] = freq;
|
_center_freq[ chan ] = freq;
|
||||||
return dev->set_center_freq( freq, dev_chan );
|
return dev->set_center_freq( freq, dev_chan );
|
||||||
|
} else { return _center_freq[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -411,10 +490,11 @@ double osmosdr_source_c_impl::set_freq_corr( double ppm, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _freq_corr[ chan ] != ppm ) {
|
if ( _freq_corr[ chan ] != ppm ) {
|
||||||
_freq_corr[ chan ] = ppm;
|
_freq_corr[ chan ] = ppm;
|
||||||
return dev->set_freq_corr( ppm, dev_chan );
|
return dev->set_freq_corr( ppm, dev_chan );
|
||||||
|
} else { return _freq_corr[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -469,13 +549,14 @@ bool osmosdr_source_c_impl::set_gain_mode( bool automatic, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _gain_mode[ chan ] != automatic ) {
|
if ( _gain_mode[ chan ] != automatic ) {
|
||||||
_gain_mode[ chan ] = automatic;
|
_gain_mode[ chan ] = automatic;
|
||||||
bool mode = dev->set_gain_mode( automatic, dev_chan );
|
bool mode = dev->set_gain_mode( automatic, dev_chan );
|
||||||
if (!automatic) // reapply gain value when switched to manual mode
|
if (!automatic) // reapply gain value when switched to manual mode
|
||||||
dev->set_gain( _gain[ chan ], dev_chan );
|
dev->set_gain( _gain[ chan ], dev_chan );
|
||||||
return mode;
|
return mode;
|
||||||
|
} else { return _gain_mode[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -497,10 +578,11 @@ double osmosdr_source_c_impl::set_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _gain[ chan ] != gain ) {
|
if ( _gain[ chan ] != gain ) {
|
||||||
_gain[ chan ] = gain;
|
_gain[ chan ] = gain;
|
||||||
return dev->set_gain( gain, dev_chan );
|
return dev->set_gain( gain, dev_chan );
|
||||||
|
} else { return _gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -544,10 +626,11 @@ double osmosdr_source_c_impl::set_if_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _if_gain[ chan ] != gain ) {
|
if ( _if_gain[ chan ] != gain ) {
|
||||||
_if_gain[ chan ] = gain;
|
_if_gain[ chan ] = gain;
|
||||||
return dev->set_if_gain( gain, dev_chan );
|
return dev->set_if_gain( gain, dev_chan );
|
||||||
|
} else { return _if_gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -558,10 +641,11 @@ double osmosdr_source_c_impl::set_bb_gain( double gain, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _bb_gain[ chan ] != gain ) {
|
if ( _bb_gain[ chan ] != gain ) {
|
||||||
_bb_gain[ chan ] = gain;
|
_bb_gain[ chan ] = gain;
|
||||||
return dev->set_bb_gain( gain, dev_chan );
|
return dev->set_bb_gain( gain, dev_chan );
|
||||||
|
} else { return _bb_gain[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -583,10 +667,11 @@ std::string osmosdr_source_c_impl::set_antenna( const std::string & antenna, siz
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _antenna[ chan ] != antenna ) {
|
if ( _antenna[ chan ] != antenna ) {
|
||||||
_antenna[ chan ] = antenna;
|
_antenna[ chan ] = antenna;
|
||||||
return dev->set_antenna( antenna, dev_chan );
|
return dev->set_antenna( antenna, dev_chan );
|
||||||
|
} else { return _antenna[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
@ -603,10 +688,28 @@ std::string osmosdr_source_c_impl::get_antenna( size_t chan )
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void osmosdr_source_c_impl::set_dc_offset_mode( int mode, size_t chan )
|
||||||
|
{
|
||||||
|
size_t channel = 0;
|
||||||
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
dev->set_dc_offset_mode( mode, dev_chan );
|
||||||
|
}
|
||||||
|
|
||||||
|
void osmosdr_source_c_impl::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
|
{
|
||||||
|
size_t channel = 0;
|
||||||
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
dev->set_dc_offset( offset, dev_chan );
|
||||||
|
}
|
||||||
|
|
||||||
void osmosdr_source_c_impl::set_iq_balance_mode( int mode, size_t chan )
|
void osmosdr_source_c_impl::set_iq_balance_mode( int mode, size_t chan )
|
||||||
{
|
{
|
||||||
#ifdef HAVE_IQBALANCE
|
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
|
#ifdef HAVE_IQBALANCE
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
|
||||||
if ( chan == channel++ ) {
|
if ( chan == channel++ ) {
|
||||||
|
@ -636,13 +739,18 @@ void osmosdr_source_c_impl::set_iq_balance_mode( int mode, size_t chan )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
return dev->set_iq_balance_mode( mode, dev_chan );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void osmosdr_source_c_impl::set_iq_balance( const std::complex<double> &correction, size_t chan )
|
void osmosdr_source_c_impl::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
{
|
{
|
||||||
#ifdef HAVE_IQBALANCE
|
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
|
#ifdef HAVE_IQBALANCE
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
|
||||||
if ( chan == channel++ ) {
|
if ( chan == channel++ ) {
|
||||||
|
@ -651,13 +759,18 @@ void osmosdr_source_c_impl::set_iq_balance( const std::complex<double> &correcti
|
||||||
iqbalance_fix_cc *fix = _iq_fix[chan];
|
iqbalance_fix_cc *fix = _iq_fix[chan];
|
||||||
|
|
||||||
if ( opt->period() == 0 ) { /* automatic optimization desabled */
|
if ( opt->period() == 0 ) { /* automatic optimization desabled */
|
||||||
fix->set_mag( correction.real() );
|
fix->set_mag( balance.real() );
|
||||||
fix->set_phase( correction.imag() );
|
fix->set_phase( balance.imag() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
|
if ( chan == channel++ )
|
||||||
|
return dev->set_iq_balance( balance, dev_chan );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -666,10 +779,11 @@ double osmosdr_source_c_impl::set_bandwidth( double bandwidth, size_t chan )
|
||||||
size_t channel = 0;
|
size_t channel = 0;
|
||||||
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
|
||||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||||
if ( chan == channel++ )
|
if ( chan == channel++ ) {
|
||||||
if ( _bandwidth[ chan ] != bandwidth ) {
|
if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
|
||||||
_bandwidth[ chan ] = bandwidth;
|
_bandwidth[ chan ] = bandwidth;
|
||||||
return dev->set_bandwidth( bandwidth, dev_chan );
|
return dev->set_bandwidth( bandwidth, dev_chan );
|
||||||
|
} else { return _bandwidth[ chan ]; }
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -36,6 +36,8 @@ class osmosdr_source_c_impl : public osmosdr_source_c
|
||||||
public:
|
public:
|
||||||
size_t get_num_channels( void );
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
bool seek( long seek_point, int whence, size_t chan );
|
||||||
|
|
||||||
osmosdr::meta_range_t get_sample_rates( void );
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
double set_sample_rate( double rate );
|
double set_sample_rate( double rate );
|
||||||
double get_sample_rate( void );
|
double get_sample_rate( void );
|
||||||
|
@ -63,8 +65,11 @@ public:
|
||||||
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_dc_offset_mode( int mode, size_t chan = 0 );
|
||||||
|
void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 );
|
||||||
|
|
||||||
void set_iq_balance_mode( int mode, size_t chan = 0 );
|
void set_iq_balance_mode( int mode, size_t chan = 0 );
|
||||||
void set_iq_balance( const std::complex<double> &correction, size_t chan = 0 );
|
void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 );
|
||||||
|
|
||||||
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
double get_bandwidth( size_t chan = 0 );
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
@ -79,6 +84,7 @@ private:
|
||||||
|
|
||||||
std::vector< osmosdr_src_iface * > _devs;
|
std::vector< osmosdr_src_iface * > _devs;
|
||||||
|
|
||||||
|
/* cache to prevent multiple device calls with the same value coming from grc */
|
||||||
double _sample_rate;
|
double _sample_rate;
|
||||||
std::map< size_t, double > _center_freq;
|
std::map< size_t, double > _center_freq;
|
||||||
std::map< size_t, double > _freq_corr;
|
std::map< size_t, double > _freq_corr;
|
||||||
|
|
|
@ -41,6 +41,15 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual size_t get_num_channels( void ) = 0;
|
virtual size_t get_num_channels( void ) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief seek file to \p seek_point relative to \p whence
|
||||||
|
*
|
||||||
|
* \param seek_point sample offset in file
|
||||||
|
* \param whence one of SEEK_SET, SEEK_CUR, SEEK_END (man fseek)
|
||||||
|
* \return true on success
|
||||||
|
*/
|
||||||
|
virtual bool seek( long seek_point, int whence, size_t chan = 0 ) { return false; }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Get the possible sample rates for the underlying radio hardware.
|
* Get the possible sample rates for the underlying radio hardware.
|
||||||
* \return a range of rates in Sps
|
* \return a range of rates in Sps
|
||||||
|
@ -217,6 +226,30 @@ public:
|
||||||
*/
|
*/
|
||||||
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
virtual std::string get_antenna( size_t chan = 0 ) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set the RX frontend DC correction mode.
|
||||||
|
* The automatic correction subtracts out the long-run average.
|
||||||
|
*
|
||||||
|
* When disabled, the averaging option operation is reset.
|
||||||
|
* Once in Manual mode, the average value will be held constant until
|
||||||
|
* the user re-enables the automatic correction or overrides the
|
||||||
|
* value by manually setting the offset.
|
||||||
|
*
|
||||||
|
* \param mode dc offset correction mode: 0 = Off, 1 = Manual, 2 = Automatic
|
||||||
|
* \param chan the channel index 0 to N-1
|
||||||
|
*/
|
||||||
|
virtual void set_dc_offset_mode( int mode, size_t chan = 0 ) { }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Set a constant DC offset value.
|
||||||
|
* The value is complex to control both I and Q.
|
||||||
|
* Only set this when automatic correction is disabled.
|
||||||
|
*
|
||||||
|
* \param offset the dc offset (1.0 is full-scale)
|
||||||
|
* \param chan the channel index 0 to N-1
|
||||||
|
*/
|
||||||
|
virtual void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 ) { }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the RX frontend IQ balance mode.
|
* Set the RX frontend IQ balance mode.
|
||||||
*
|
*
|
||||||
|
@ -229,14 +262,14 @@ public:
|
||||||
* Set the RX frontend IQ balance correction.
|
* Set the RX frontend IQ balance correction.
|
||||||
* Use this to adjust the magnitude and phase of I and Q.
|
* Use this to adjust the magnitude and phase of I and Q.
|
||||||
*
|
*
|
||||||
* \param correction the complex correction value
|
* \param balance the complex correction value
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
*/
|
*/
|
||||||
virtual void set_iq_balance( const std::complex<double> &correction, size_t chan = 0 ) { }
|
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) { }
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Set the bandpass filter on the radio frontend.
|
* Set the bandpass filter on the radio frontend.
|
||||||
* \param bandwidth the filter bandwidth in Hz
|
* \param bandwidth the filter bandwidth in Hz, set to 0 for automatic selection
|
||||||
* \param chan the channel index 0 to N-1
|
* \param chan the channel index 0 to N-1
|
||||||
* \return the actual filter bandwidth in Hz
|
* \return the actual filter bandwidth in Hz
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Copyright 2012 Free Software Foundation, Inc.
|
||||||
|
#
|
||||||
|
# This file is part of GNU Radio
|
||||||
|
#
|
||||||
|
# GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
# any later version.
|
||||||
|
#
|
||||||
|
# GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
# the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# This file included, use CMake directory variables
|
||||||
|
########################################################################
|
||||||
|
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(rfspace_srcs
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/rfspace_source_c.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
########################################################################
|
||||||
|
# Append gnuradio-osmosdr library sources
|
||||||
|
########################################################################
|
||||||
|
list(APPEND gr_osmosdr_srcs ${rfspace_srcs})
|
||||||
|
#list(APPEND gr_osmosdr_libs ...)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,176 @@
|
||||||
|
/* -*- c++ -*- */
|
||||||
|
/*
|
||||||
|
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||||
|
*
|
||||||
|
* GNU Radio is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3, or (at your option)
|
||||||
|
* any later version.
|
||||||
|
*
|
||||||
|
* GNU Radio is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with GNU Radio; see the file COPYING. If not, write to
|
||||||
|
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDED_RFSPACE_SOURCE_C_H
|
||||||
|
#define INCLUDED_RFSPACE_SOURCE_C_H
|
||||||
|
|
||||||
|
//#define USE_ASIO
|
||||||
|
|
||||||
|
#ifdef USE_ASIO
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <boost/circular_buffer.hpp>
|
||||||
|
#include <boost/thread/mutex.hpp>
|
||||||
|
#include <boost/thread/condition_variable.hpp>
|
||||||
|
|
||||||
|
#include <gruel/thread.h>
|
||||||
|
#include <gr_block.h>
|
||||||
|
#include <gr_sync_block.h>
|
||||||
|
|
||||||
|
#include "osmosdr/osmosdr_ranges.h"
|
||||||
|
#include "osmosdr_src_iface.h"
|
||||||
|
|
||||||
|
#ifdef USE_ASIO
|
||||||
|
using boost::asio::ip::tcp;
|
||||||
|
using boost::asio::ip::udp;
|
||||||
|
#endif
|
||||||
|
class rfspace_source_c;
|
||||||
|
|
||||||
|
#ifndef SOCKET
|
||||||
|
#define SOCKET int
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||||
|
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||||
|
* us transparent reference counting, which greatly simplifies storage
|
||||||
|
* management issues. This is especially helpful in our hybrid
|
||||||
|
* C++ / Python system.
|
||||||
|
*
|
||||||
|
* See http://www.boost.org/libs/smart_ptr/smart_ptr.htm
|
||||||
|
*
|
||||||
|
* As a convention, the _sptr suffix indicates a boost::shared_ptr
|
||||||
|
*/
|
||||||
|
typedef boost::shared_ptr<rfspace_source_c> rfspace_source_c_sptr;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* \brief Return a shared_ptr to a new instance of rfspace_source_c.
|
||||||
|
*
|
||||||
|
* To avoid accidental use of raw pointers, rfspace_source_c's
|
||||||
|
* constructor is private. rfspace_make_source_c is the public
|
||||||
|
* interface for creating new instances.
|
||||||
|
*/
|
||||||
|
rfspace_source_c_sptr make_rfspace_source_c (const std::string & args = "");
|
||||||
|
|
||||||
|
class rfspace_source_c :
|
||||||
|
public gr_sync_block,
|
||||||
|
public osmosdr_src_iface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// The friend declaration allows rfspace_make_source_c to
|
||||||
|
// access the private constructor.
|
||||||
|
friend rfspace_source_c_sptr make_rfspace_source_c (const std::string & args);
|
||||||
|
|
||||||
|
rfspace_source_c (const std::string & args); // private constructor
|
||||||
|
|
||||||
|
public:
|
||||||
|
~rfspace_source_c (); // public destructor
|
||||||
|
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
int work(int noutput_items,
|
||||||
|
gr_vector_const_void_star &input_items,
|
||||||
|
gr_vector_void_star &output_items );
|
||||||
|
|
||||||
|
static std::vector< std::string > get_devices( bool fake = false );
|
||||||
|
|
||||||
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
osmosdr::meta_range_t get_sample_rates( void );
|
||||||
|
double set_sample_rate( double rate );
|
||||||
|
double get_sample_rate( void );
|
||||||
|
|
||||||
|
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
|
||||||
|
double set_center_freq( double freq, size_t chan = 0 );
|
||||||
|
double get_center_freq( size_t chan = 0 );
|
||||||
|
double set_freq_corr( double ppm, size_t chan = 0 );
|
||||||
|
double get_freq_corr( size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector<std::string> get_gain_names( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
|
||||||
|
osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
|
||||||
|
bool set_gain_mode( bool automatic, size_t chan = 0 );
|
||||||
|
bool get_gain_mode( size_t chan = 0 );
|
||||||
|
double set_gain( double gain, size_t chan = 0 );
|
||||||
|
double set_gain( double gain, const std::string & name, size_t chan = 0 );
|
||||||
|
double get_gain( size_t chan = 0 );
|
||||||
|
double get_gain( const std::string & name, size_t chan = 0 );
|
||||||
|
|
||||||
|
std::vector< std::string > get_antennas( size_t chan = 0 );
|
||||||
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
|
||||||
|
private: /* functions */
|
||||||
|
void apply_channel( unsigned char *cmd, size_t chan = 0 );
|
||||||
|
|
||||||
|
bool transaction( const unsigned char *cmd, size_t size );
|
||||||
|
|
||||||
|
bool transaction( const unsigned char *cmd, size_t size,
|
||||||
|
std::vector< unsigned char > &response );
|
||||||
|
|
||||||
|
void usb_read_task();
|
||||||
|
|
||||||
|
private: /* members */
|
||||||
|
enum radio_type
|
||||||
|
{
|
||||||
|
RADIO_UNKNOWN = 0,
|
||||||
|
RFSPACE_SDR_IQ,
|
||||||
|
RFSPACE_SDR_IP,
|
||||||
|
RFSPACE_NETSDR
|
||||||
|
};
|
||||||
|
|
||||||
|
radio_type _radio;
|
||||||
|
|
||||||
|
#ifdef USE_ASIO
|
||||||
|
boost::asio::io_service _io_service;
|
||||||
|
tcp::resolver _resolver;
|
||||||
|
tcp::socket _t;
|
||||||
|
udp::socket _u;
|
||||||
|
#else
|
||||||
|
SOCKET _tcp;
|
||||||
|
SOCKET _udp;
|
||||||
|
#endif
|
||||||
|
int _usb;
|
||||||
|
bool _running;
|
||||||
|
bool _keep_running;
|
||||||
|
uint16_t _sequence;
|
||||||
|
|
||||||
|
size_t _nchan;
|
||||||
|
double _sample_rate;
|
||||||
|
double _bandwidth;
|
||||||
|
|
||||||
|
gruel::thread _thread;
|
||||||
|
bool _run_usb_read_task;
|
||||||
|
|
||||||
|
boost::circular_buffer<gr_complex> *_fifo;
|
||||||
|
boost::mutex _fifo_lock;
|
||||||
|
boost::condition_variable _samp_avail;
|
||||||
|
|
||||||
|
std::vector< unsigned char > _resp;
|
||||||
|
boost::mutex _resp_lock;
|
||||||
|
boost::condition_variable _resp_avail;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* INCLUDED_RFSPACE_SOURCE_C_H */
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
${LIBRTLSDR_INCLUDE_DIR}
|
${LIBRTLSDR_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rtl_srcs
|
set(rtl_srcs
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
||||||
#define BUF_NUM 32
|
#define BUF_NUM 15
|
||||||
#define BUF_SKIP 1 // buffers to skip due to initial garbage
|
#define BUF_SKIP 1 // buffers to skip due to initial garbage
|
||||||
|
|
||||||
#define BYTES_PER_SAMPLE 2 // rtl device delivers 8 bit unsigned IQ data
|
#define BYTES_PER_SAMPLE 2 // rtl device delivers 8 bit unsigned IQ data
|
||||||
|
@ -84,7 +84,7 @@ rtl_source_c::rtl_source_c (const std::string &args)
|
||||||
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
|
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
|
||||||
_dev(NULL),
|
_dev(NULL),
|
||||||
_buf(NULL),
|
_buf(NULL),
|
||||||
_running(true),
|
_running(false),
|
||||||
_no_tuner(false),
|
_no_tuner(false),
|
||||||
_auto_gain(false),
|
_auto_gain(false),
|
||||||
_if_gain(0),
|
_if_gain(0),
|
||||||
|
@ -101,14 +101,18 @@ rtl_source_c::rtl_source_c (const std::string &args)
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
|
||||||
if (dict.count("rtl")) {
|
if (dict.count("rtl")) {
|
||||||
if ( (index = rtlsdr_get_index_by_serial( dict["rtl"].c_str() )) >= 0 ) {
|
std::string value = dict["rtl"];
|
||||||
|
|
||||||
|
if ( (index = rtlsdr_get_index_by_serial( value.c_str() )) >= 0 ) {
|
||||||
dev_index = index; /* use the resolved index value */
|
dev_index = index; /* use the resolved index value */
|
||||||
} else { /* use the numeric value of the argument */
|
} else { /* use the numeric value of the argument */
|
||||||
|
if ( value.length() ) {
|
||||||
try {
|
try {
|
||||||
dev_index = boost::lexical_cast< unsigned int >( dict["rtl"] );
|
dev_index = boost::lexical_cast< unsigned int >( value );
|
||||||
} catch ( std::exception &ex ) {
|
} catch ( std::exception &ex ) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Failed to use '" + dict["rtl"] + "' as index: " + ex.what());
|
"Failed to use '" + value + "' as index: " + ex.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,11 +174,11 @@ rtl_source_c::rtl_source_c (const std::string &args)
|
||||||
// create a lookup table for gr_complex values
|
// create a lookup table for gr_complex values
|
||||||
for (unsigned int i = 0; i <= 0xffff; i++) {
|
for (unsigned int i = 0; i <= 0xffff; i++) {
|
||||||
#ifdef BOOST_LITTLE_ENDIAN
|
#ifdef BOOST_LITTLE_ENDIAN
|
||||||
_lut.push_back( gr_complex( (float(i & 0xff) - 127.5f) * (1.0f/128.0f),
|
_lut.push_back( gr_complex( (float(i & 0xff) - 127.4f) * (1.0f/128.0f),
|
||||||
(float(i >> 8) - 127.5f) * (1.0f/128.0f) ) );
|
(float(i >> 8) - 127.4f) * (1.0f/128.0f) ) );
|
||||||
#else // BOOST_BIG_ENDIAN
|
#else // BOOST_BIG_ENDIAN
|
||||||
_lut.push_back( gr_complex( (float(i >> 8) - 127.5f) * (1.0f/128.0f),
|
_lut.push_back( gr_complex( (float(i >> 8) - 127.4f) * (1.0f/128.0f),
|
||||||
(float(i & 0xff) - 127.5f) * (1.0f/128.0f) ) );
|
(float(i & 0xff) - 127.4f) * (1.0f/128.0f) ) );
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,8 +236,6 @@ rtl_source_c::rtl_source_c (const std::string &args)
|
||||||
for(unsigned int i = 0; i < _buf_num; ++i)
|
for(unsigned int i = 0; i < _buf_num; ++i)
|
||||||
_buf[i] = (unsigned short *) malloc(_buf_len);
|
_buf[i] = (unsigned short *) malloc(_buf_len);
|
||||||
}
|
}
|
||||||
|
|
||||||
_thread = gruel::thread(_rtlsdr_wait, this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -242,9 +244,13 @@ rtl_source_c::rtl_source_c (const std::string &args)
|
||||||
rtl_source_c::~rtl_source_c ()
|
rtl_source_c::~rtl_source_c ()
|
||||||
{
|
{
|
||||||
if (_dev) {
|
if (_dev) {
|
||||||
|
if (_running)
|
||||||
|
{
|
||||||
_running = false;
|
_running = false;
|
||||||
rtlsdr_cancel_async( _dev );
|
rtlsdr_cancel_async( _dev );
|
||||||
_thread.join();
|
_thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
rtlsdr_close( _dev );
|
rtlsdr_close( _dev );
|
||||||
_dev = NULL;
|
_dev = NULL;
|
||||||
}
|
}
|
||||||
|
@ -260,6 +266,24 @@ rtl_source_c::~rtl_source_c ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool rtl_source_c::start()
|
||||||
|
{
|
||||||
|
_running = true;
|
||||||
|
_thread = gruel::thread(_rtlsdr_wait, this);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool rtl_source_c::stop()
|
||||||
|
{
|
||||||
|
_running = false;
|
||||||
|
if (_dev)
|
||||||
|
rtlsdr_cancel_async( _dev );
|
||||||
|
_thread.join();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void rtl_source_c::_rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
|
void rtl_source_c::_rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
|
||||||
{
|
{
|
||||||
rtl_source_c *obj = (rtl_source_c *)ctx;
|
rtl_source_c *obj = (rtl_source_c *)ctx;
|
||||||
|
@ -297,7 +321,7 @@ void rtl_source_c::_rtlsdr_wait(rtl_source_c *obj)
|
||||||
|
|
||||||
void rtl_source_c::rtlsdr_wait()
|
void rtl_source_c::rtlsdr_wait()
|
||||||
{
|
{
|
||||||
int ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, 0, _buf_len );
|
int ret = rtlsdr_read_async( _dev, _rtlsdr_callback, (void *)this, _buf_num, _buf_len );
|
||||||
|
|
||||||
_running = false;
|
_running = false;
|
||||||
|
|
||||||
|
@ -409,6 +433,7 @@ osmosdr::meta_range_t rtl_source_c::get_sample_rates()
|
||||||
range += osmosdr::range_t( 2000000 ); // known to work
|
range += osmosdr::range_t( 2000000 ); // known to work
|
||||||
range += osmosdr::range_t( 2048000 ); // known to work
|
range += osmosdr::range_t( 2048000 ); // known to work
|
||||||
range += osmosdr::range_t( 2400000 ); // known to work
|
range += osmosdr::range_t( 2400000 ); // known to work
|
||||||
|
range += osmosdr::range_t( 2560000 ); // known to work
|
||||||
// range += osmosdr::range_t( 2600000 ); // may work
|
// range += osmosdr::range_t( 2600000 ); // may work
|
||||||
// range += osmosdr::range_t( 2800000 ); // may work
|
// range += osmosdr::range_t( 2800000 ); // may work
|
||||||
// range += osmosdr::range_t( 3000000 ); // may work
|
// range += osmosdr::range_t( 3000000 ); // may work
|
||||||
|
@ -460,6 +485,8 @@ osmosdr::freq_range_t rtl_source_c::get_freq_range( size_t chan )
|
||||||
range += osmosdr::range_t( 438e6, 924e6 );
|
range += osmosdr::range_t( 438e6, 924e6 );
|
||||||
} else if ( tuner == RTLSDR_TUNER_R820T ) {
|
} else if ( tuner == RTLSDR_TUNER_R820T ) {
|
||||||
range += osmosdr::range_t( 24e6, 1766e6 );
|
range += osmosdr::range_t( 24e6, 1766e6 );
|
||||||
|
} else if ( tuner == RTLSDR_TUNER_R828D ) {
|
||||||
|
range += osmosdr::range_t( 24e6, 1766e6 );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -112,6 +112,10 @@ public:
|
||||||
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void _rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx);
|
static void _rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx);
|
||||||
void rtlsdr_callback(unsigned char *buf, uint32_t len);
|
void rtlsdr_callback(unsigned char *buf, uint32_t len);
|
||||||
|
|
|
@ -47,6 +47,8 @@ static std::string get_tuner_name( enum rtlsdr_tuner tuner_type )
|
||||||
return "FC2580";
|
return "FC2580";
|
||||||
else if ( RTLSDR_TUNER_R820T == tuner_type )
|
else if ( RTLSDR_TUNER_R820T == tuner_type )
|
||||||
return "R820T";
|
return "R820T";
|
||||||
|
else if ( RTLSDR_TUNER_R828D == tuner_type )
|
||||||
|
return "R828D";
|
||||||
else
|
else
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
@ -147,13 +149,16 @@ std::string rtl_tcp_source_c::name()
|
||||||
return "RTL TCP Client";
|
return "RTL TCP Client";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> rtl_tcp_source_c::get_devices()
|
std::vector<std::string> rtl_tcp_source_c::get_devices( bool fake )
|
||||||
{
|
{
|
||||||
std::vector<std::string> devices;
|
std::vector<std::string> devices;
|
||||||
|
|
||||||
|
if ( fake )
|
||||||
|
{
|
||||||
std::string args = "rtl_tcp=localhost:1234";
|
std::string args = "rtl_tcp=localhost:1234";
|
||||||
args += ",label='RTL-SDR Spectrum Server'";
|
args += ",label='RTL-SDR Spectrum Server'";
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
|
}
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
@ -175,6 +180,7 @@ osmosdr::meta_range_t rtl_tcp_source_c::get_sample_rates( void )
|
||||||
range += osmosdr::range_t( 2000000 ); // known to work
|
range += osmosdr::range_t( 2000000 ); // known to work
|
||||||
range += osmosdr::range_t( 2048000 ); // known to work
|
range += osmosdr::range_t( 2048000 ); // known to work
|
||||||
range += osmosdr::range_t( 2400000 ); // known to work
|
range += osmosdr::range_t( 2400000 ); // known to work
|
||||||
|
range += osmosdr::range_t( 2560000 ); // known to work
|
||||||
// range += osmosdr::range_t( 2600000 ); // may work
|
// range += osmosdr::range_t( 2600000 ); // may work
|
||||||
// range += osmosdr::range_t( 2800000 ); // may work
|
// range += osmosdr::range_t( 2800000 ); // may work
|
||||||
// range += osmosdr::range_t( 3000000 ); // may work
|
// range += osmosdr::range_t( 3000000 ); // may work
|
||||||
|
@ -220,6 +226,8 @@ osmosdr::freq_range_t rtl_tcp_source_c::get_freq_range( size_t chan )
|
||||||
range += osmosdr::range_t( 438e6, 924e6 );
|
range += osmosdr::range_t( 438e6, 924e6 );
|
||||||
} else if ( tuner == RTLSDR_TUNER_R820T ) {
|
} else if ( tuner == RTLSDR_TUNER_R820T ) {
|
||||||
range += osmosdr::range_t( 24e6, 1766e6 );
|
range += osmosdr::range_t( 24e6, 1766e6 );
|
||||||
|
} else if ( tuner == RTLSDR_TUNER_R828D ) {
|
||||||
|
range += osmosdr::range_t( 24e6, 1766e6 );
|
||||||
} else {
|
} else {
|
||||||
range += osmosdr::range_t( 52e6, 2.2e9 ); // assume E4000 tuner
|
range += osmosdr::range_t( 52e6, 2.2e9 ); // assume E4000 tuner
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@ public:
|
||||||
|
|
||||||
std::string name();
|
std::string name();
|
||||||
|
|
||||||
static std::vector< std::string > get_devices();
|
static std::vector< std::string > get_devices( bool fake = false );
|
||||||
|
|
||||||
size_t get_num_channels( void );
|
size_t get_num_channels( void );
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ rtl_tcp_source_f::rtl_tcp_source_f(size_t itemsize,
|
||||||
d_temp_buff = new unsigned char[d_payload_size]; // allow it to hold up to payload_size bytes
|
d_temp_buff = new unsigned char[d_payload_size]; // allow it to hold up to payload_size bytes
|
||||||
d_LUT= new float[0xff+1];
|
d_LUT= new float[0xff+1];
|
||||||
for(int i=0; i <=(0xff);++i){
|
for(int i=0; i <=(0xff);++i){
|
||||||
d_LUT[i] = (((float)(i&0xff))-127.5f)*(1.0f/128.0f);
|
d_LUT[i] = (((float)(i&0xff))-127.4f)*(1.0f/128.0f);
|
||||||
}
|
}
|
||||||
// create socket
|
// create socket
|
||||||
d_socket = socket(ip_src->ai_family, ip_src->ai_socktype,
|
d_socket = socket(ip_src->ai_family, ip_src->ai_socktype,
|
||||||
|
@ -236,8 +236,7 @@ int rtl_tcp_source_f::work (int noutput_items,
|
||||||
gr_vector_void_star &output_items)
|
gr_vector_void_star &output_items)
|
||||||
{
|
{
|
||||||
float *out = (float *) output_items[0];
|
float *out = (float *) output_items[0];
|
||||||
ssize_t r=0, nbytes=0, bytes_received=0;
|
ssize_t r = 0;
|
||||||
ssize_t total_bytes = (ssize_t)(d_itemsize*noutput_items);
|
|
||||||
|
|
||||||
int bytesleft = noutput_items;
|
int bytesleft = noutput_items;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
|
|
|
@ -51,7 +51,8 @@ enum rtlsdr_tuner {
|
||||||
RTLSDR_TUNER_FC0012,
|
RTLSDR_TUNER_FC0012,
|
||||||
RTLSDR_TUNER_FC0013,
|
RTLSDR_TUNER_FC0013,
|
||||||
RTLSDR_TUNER_FC2580,
|
RTLSDR_TUNER_FC2580,
|
||||||
RTLSDR_TUNER_R820T
|
RTLSDR_TUNER_R820T,
|
||||||
|
RTLSDR_TUNER_R828D
|
||||||
};
|
};
|
||||||
|
|
||||||
class rtl_tcp_source_f;
|
class rtl_tcp_source_f;
|
||||||
|
|
|
@ -30,6 +30,9 @@
|
||||||
|
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_precision_time (long long *secs, double *fracs);
|
||||||
|
|
||||||
uhd_sink_c_sptr make_uhd_sink_c(const std::string &args)
|
uhd_sink_c_sptr make_uhd_sink_c(const std::string &args)
|
||||||
{
|
{
|
||||||
return gnuradio::get_initial_sptr(new uhd_sink_c(args));
|
return gnuradio::get_initial_sptr(new uhd_sink_c(args));
|
||||||
|
@ -39,10 +42,17 @@ uhd_sink_c::uhd_sink_c(const std::string &args) :
|
||||||
gr_hier_block2("uhd_sink_c",
|
gr_hier_block2("uhd_sink_c",
|
||||||
args_to_io_signature(args),
|
args_to_io_signature(args),
|
||||||
gr_make_io_signature (0, 0, 0)),
|
gr_make_io_signature (0, 0, 0)),
|
||||||
|
_center_freq(0.0f),
|
||||||
|
_freq_corr(0.0f),
|
||||||
_lo_offset(0.0f)
|
_lo_offset(0.0f)
|
||||||
{
|
{
|
||||||
size_t nchan = 1;
|
size_t nchan = 1;
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
uhd::stream_args_t stream_args("fc32", "sc16");
|
||||||
|
std::string extra_args;
|
||||||
|
std::vector <std::string> extra_list;
|
||||||
|
extra_list.push_back ("peak");
|
||||||
|
extra_list.push_back ("fullscale");
|
||||||
|
|
||||||
if (dict.count("nchan"))
|
if (dict.count("nchan"))
|
||||||
nchan = boost::lexical_cast< size_t >( dict["nchan"] );
|
nchan = boost::lexical_cast< size_t >( dict["nchan"] );
|
||||||
|
@ -58,7 +68,13 @@ uhd_sink_c::uhd_sink_c(const std::string &args) :
|
||||||
if ( "uhd" != entry.first &&
|
if ( "uhd" != entry.first &&
|
||||||
"nchan" != entry.first &&
|
"nchan" != entry.first &&
|
||||||
"subdev" != entry.first &&
|
"subdev" != entry.first &&
|
||||||
"lo_offset" != entry.first ) {
|
"lo_offset" != entry.first &&
|
||||||
|
"otw_format" != entry.first &&
|
||||||
|
"peak" != entry.first &&
|
||||||
|
"fullscale" != entry.first &&
|
||||||
|
"refclock" != entry.first &&
|
||||||
|
"pps" != entry.first &&
|
||||||
|
"sync" != entry.first ) {
|
||||||
arguments += entry.first + "=" + entry.second + ",";
|
arguments += entry.first + "=" + entry.second + ",";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,16 +83,114 @@ uhd_sink_c::uhd_sink_c(const std::string &args) :
|
||||||
uhd::io_type_t::COMPLEX_FLOAT32,
|
uhd::io_type_t::COMPLEX_FLOAT32,
|
||||||
nchan );
|
nchan );
|
||||||
|
|
||||||
|
stream_args.cpu_format = "fc32";
|
||||||
|
stream_args.otw_format = "sc16";
|
||||||
|
|
||||||
|
for (size_t chan = 0; chan < nchan; chan++)
|
||||||
|
stream_args.channels.push_back(chan); //linear mapping
|
||||||
|
|
||||||
|
if (dict.count("otw_format") )
|
||||||
|
stream_args.otw_format = dict["otw_format"];
|
||||||
|
|
||||||
|
// There's probably a more C++/Boosty way to do this.
|
||||||
|
// look for "peak" and "fullscale" args, and make up some lovely syntax
|
||||||
|
// that will be acceptable in uhd::stream_args.args
|
||||||
|
for (unsigned int q = 0; q < extra_list.size(); q++)
|
||||||
|
{
|
||||||
|
if (dict.count(extra_list[q]) )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting " + extra_list[q] + "=" + dict[extra_list[q]] + "\n";
|
||||||
|
extra_args += extra_list[q] + "=" + dict[extra_list[q]];
|
||||||
|
if (q < (extra_list.size()-1))
|
||||||
|
{
|
||||||
|
extra_args += ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extra_args.length() > 0)
|
||||||
|
{
|
||||||
|
// Finally stuff the args
|
||||||
|
stream_args.args = extra_args;
|
||||||
|
}
|
||||||
|
|
||||||
|
_snk = uhd_make_usrp_sink ( arguments,
|
||||||
|
stream_args );
|
||||||
|
|
||||||
if (dict.count("subdev")) {
|
if (dict.count("subdev")) {
|
||||||
_snk->set_subdev_spec( dict["subdev"] );
|
_snk->set_subdev_spec( dict["subdev"] );
|
||||||
|
}
|
||||||
|
|
||||||
std::cerr << "-- Using subdev spec '" << _snk->get_subdev_spec() << "'."
|
std::cerr << "-- Using subdev spec '" << _snk->get_subdev_spec() << "'."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
if (0.0 != _lo_offset)
|
if (0.0 != _lo_offset)
|
||||||
std::cerr << "-- Using lo offset of " << _lo_offset << " Hz." << std::endl;
|
std::cerr << "-- Using lo offset of " << _lo_offset << " Hz." << std::endl;
|
||||||
|
|
||||||
|
// Fargking oogly. Needs to pull ALL_MBOARDS constant out of multi_usrp:: but I can't figure out how
|
||||||
|
// So, ALL_MBOARDS is actually just size_t(~0)
|
||||||
|
size_t ALL_MBOARDS = size_t(~0);
|
||||||
|
|
||||||
|
if (dict.count("refclock") )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting refclock: " + dict["refclock"] + "\n";
|
||||||
|
_snk->set_clock_source (dict["refclock"], ALL_MBOARDS);
|
||||||
|
|
||||||
|
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||||
|
|
||||||
|
uhd::sensor_value_t ref_locked = _snk->get_mboard_sensor("ref_locked",0);
|
||||||
|
if (!ref_locked.to_bool())
|
||||||
|
{
|
||||||
|
std::cerr << "-- WARNING: Requested ref-clock source: " << dict["refclock"] << "\n";
|
||||||
|
std::cerr << "-- Ref-clock lock sensor indicates: UNLOCKED\n";
|
||||||
|
std::cerr << "-- You may have poorer phase noise/frequency accuracy\n";
|
||||||
|
std::cerr << "-- Phase-coherence with other devices will be poor.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict.count("pps") )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting PPS source: " + dict["refclock"] + "\n";
|
||||||
|
_snk->set_time_source (dict["pps"], ALL_MBOARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set TOD across all MBOARDS to current host time
|
||||||
|
*/
|
||||||
|
if (dict.count("sync") )
|
||||||
|
{
|
||||||
|
long long seconds;
|
||||||
|
double fracts;
|
||||||
|
|
||||||
|
get_precision_time (&seconds, &fracts);
|
||||||
|
std::string st = boost::to_upper_copy(dict["sync"]);
|
||||||
|
|
||||||
|
std::cerr << "-- Setting TOD to: " << (long long)seconds << "."
|
||||||
|
<< (long long)fracts << " with method: "+st << "\n";
|
||||||
|
if (dict["sync"] == "unknown" )
|
||||||
|
{
|
||||||
|
_snk->set_time_unknown_pps (uhd::time_spec_t((time_t)seconds+1));
|
||||||
|
}
|
||||||
|
else if (dict["sync"] == "next")
|
||||||
|
{
|
||||||
|
_snk->set_time_next_pps (uhd::time_spec_t((time_t)seconds+1));
|
||||||
|
}
|
||||||
|
else if (dict["sync"] == "now")
|
||||||
|
{
|
||||||
|
fracts += 0.001;
|
||||||
|
if (fracts >= 1.0)
|
||||||
|
{
|
||||||
|
fracts -= 1.0;
|
||||||
|
seconds += 1LL;
|
||||||
|
}
|
||||||
|
_snk->set_time_now (uhd::time_spec_t((time_t)seconds, fracts), ALL_MBOARDS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "*** Not processing sync request: unknown type: " + dict["sync"] + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ( size_t i = 0; i < nchan; i++ )
|
for ( size_t i = 0; i < nchan; i++ )
|
||||||
connect( self(), i, _snk, i );
|
connect( self(), i, _snk, i );
|
||||||
}
|
}
|
||||||
|
@ -90,14 +204,18 @@ std::vector< std::string > uhd_sink_c::get_devices()
|
||||||
std::vector< std::string > devices;
|
std::vector< std::string > devices;
|
||||||
|
|
||||||
uhd::device_addr_t hint;
|
uhd::device_addr_t hint;
|
||||||
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint)) {
|
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint))
|
||||||
std::string args = "uhd,subdev='A:0'," + dev.to_string();
|
{
|
||||||
|
std::string args = "uhd," + dev.to_string();
|
||||||
|
|
||||||
|
std::string type = dev.cast< std::string >("type", "usrp");
|
||||||
|
std::string name = dev.cast< std::string >("name", "");
|
||||||
|
std::string serial = dev.cast< std::string >("serial", "");
|
||||||
|
|
||||||
std::string label = "Ettus";
|
std::string label = "Ettus";
|
||||||
|
|
||||||
std::string type = dev.cast< std::string >("type", "USRP");
|
if ( "umtrx" == type )
|
||||||
std::string name = dev.cast< std::string >("name", "");
|
label = "Fairwaves";
|
||||||
std::string serial = dev.cast< std::string >("serial", "");
|
|
||||||
|
|
||||||
if (type.length()) {
|
if (type.length()) {
|
||||||
boost::to_upper(type);
|
boost::to_upper(type);
|
||||||
|
@ -105,7 +223,7 @@ std::vector< std::string > uhd_sink_c::get_devices()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.length())
|
if (name.length())
|
||||||
label += " " + name;
|
label += " (" + name + ")";
|
||||||
|
|
||||||
if (serial.length())
|
if (serial.length())
|
||||||
label += " " + serial;
|
label += " " + serial;
|
||||||
|
@ -115,9 +233,6 @@ std::vector< std::string > uhd_sink_c::get_devices()
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
}
|
}
|
||||||
|
|
||||||
//devices.clear();
|
|
||||||
//devices.push_back( "uhd,type=usrp1,label='Ettus USRP'" );
|
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +251,8 @@ std::string uhd_sink_c::name()
|
||||||
|
|
||||||
size_t uhd_sink_c::get_num_channels()
|
size_t uhd_sink_c::get_num_channels()
|
||||||
{
|
{
|
||||||
return _snk->get_device()->get_rx_num_channels();
|
// return _snk->get_device()->get_rx_num_channels();
|
||||||
|
return input_signature()->max_streams();
|
||||||
}
|
}
|
||||||
|
|
||||||
osmosdr::meta_range_t uhd_sink_c::get_sample_rates( void )
|
osmosdr::meta_range_t uhd_sink_c::get_sample_rates( void )
|
||||||
|
@ -172,13 +288,16 @@ osmosdr::freq_range_t uhd_sink_c::get_freq_range( size_t chan )
|
||||||
|
|
||||||
double uhd_sink_c::set_center_freq( double freq, size_t chan )
|
double uhd_sink_c::set_center_freq( double freq, size_t chan )
|
||||||
{
|
{
|
||||||
//_snk->set_center_freq(freq, chan);
|
#define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001))
|
||||||
|
|
||||||
|
double corr_freq = APPLY_PPM_CORR( freq, _freq_corr );
|
||||||
|
|
||||||
// advanced tuning with tune_request_t
|
// advanced tuning with tune_request_t
|
||||||
uhd::tune_request_t tune_req(freq, _lo_offset);
|
uhd::tune_request_t tune_req(corr_freq, _lo_offset);
|
||||||
|
|
||||||
_snk->set_center_freq(tune_req, chan);
|
_snk->set_center_freq(tune_req, chan);
|
||||||
|
|
||||||
|
_center_freq = freq;
|
||||||
|
|
||||||
return get_center_freq(chan);
|
return get_center_freq(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +308,16 @@ double uhd_sink_c::get_center_freq( size_t chan )
|
||||||
|
|
||||||
double uhd_sink_c::set_freq_corr( double ppm, size_t chan )
|
double uhd_sink_c::set_freq_corr( double ppm, size_t chan )
|
||||||
{
|
{
|
||||||
|
_freq_corr = ppm;
|
||||||
|
|
||||||
|
set_center_freq( _center_freq );
|
||||||
|
|
||||||
return get_freq_corr(chan);
|
return get_freq_corr(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_sink_c::get_freq_corr( size_t chan )
|
double uhd_sink_c::get_freq_corr( size_t chan )
|
||||||
{
|
{
|
||||||
return 0; // frequency correction is not supported with UHD
|
return _freq_corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> uhd_sink_c::get_gain_names( size_t chan )
|
std::vector<std::string> uhd_sink_c::get_gain_names( size_t chan )
|
||||||
|
@ -262,3 +385,65 @@ std::string uhd_sink_c::get_antenna( size_t chan )
|
||||||
{
|
{
|
||||||
return _snk->get_antenna(chan);
|
return _snk->get_antenna(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uhd_sink_c::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_snk->set_dc_offset( offset, chan );
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uhd_sink_c::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_snk->set_iq_balance( balance, chan );
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double uhd_sink_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
{
|
||||||
|
_snk->set_bandwidth(bandwidth, chan);
|
||||||
|
|
||||||
|
return _snk->get_bandwidth(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
double uhd_sink_c::get_bandwidth( size_t chan )
|
||||||
|
{
|
||||||
|
return _snk->get_bandwidth(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t uhd_sink_c::get_bandwidth_range( size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::freq_range_t bandwidths;
|
||||||
|
|
||||||
|
BOOST_FOREACH( uhd::range_t bw, _snk->get_bandwidth_range(chan) )
|
||||||
|
bandwidths += osmosdr::range_t( bw.start(), bw.stop(), bw.step() );
|
||||||
|
|
||||||
|
return bandwidths;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_precision_time (long long *secs, double *fracs)
|
||||||
|
{
|
||||||
|
boost::posix_time::ptime now;
|
||||||
|
boost::posix_time::ptime epoch(boost::posix_time::from_time_t((time_t)0));
|
||||||
|
boost::posix_time::time_duration diff;
|
||||||
|
double psecs;
|
||||||
|
double fracts;
|
||||||
|
|
||||||
|
now = boost::posix_time::microsec_clock::universal_time();
|
||||||
|
|
||||||
|
diff = now - epoch;
|
||||||
|
psecs = (diff.total_milliseconds() / 1.0e3);
|
||||||
|
|
||||||
|
fracts = psecs - (long long)psecs;
|
||||||
|
fracts *= 1.0e3;
|
||||||
|
|
||||||
|
*secs = (long long)psecs;
|
||||||
|
*fracs = fracts;
|
||||||
|
}
|
||||||
|
|
|
@ -71,7 +71,17 @@ public:
|
||||||
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
double _center_freq;
|
||||||
|
double _freq_corr;
|
||||||
double _lo_offset;
|
double _lo_offset;
|
||||||
boost::shared_ptr<uhd_usrp_sink> _snk;
|
boost::shared_ptr<uhd_usrp_sink> _snk;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,12 +21,16 @@
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/assign.hpp>
|
#include <boost/assign.hpp>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
#include <boost/thread.hpp>
|
||||||
|
|
||||||
//#include <uhd/property_tree.hpp>
|
//#include <uhd/property_tree.hpp>
|
||||||
|
|
||||||
#include "osmosdr_arg_helpers.h"
|
#include "osmosdr_arg_helpers.h"
|
||||||
|
|
||||||
#include "uhd_source_c.h"
|
#include "uhd_source_c.h"
|
||||||
|
#include "osmosdr/osmosdr_source_c.h"
|
||||||
|
|
||||||
|
static void get_precision_time (long long *secs, double *fracs);
|
||||||
|
|
||||||
using namespace boost::assign;
|
using namespace boost::assign;
|
||||||
|
|
||||||
|
@ -39,10 +43,17 @@ uhd_source_c::uhd_source_c(const std::string &args) :
|
||||||
gr_hier_block2("uhd_source_c",
|
gr_hier_block2("uhd_source_c",
|
||||||
gr_make_io_signature (0, 0, 0),
|
gr_make_io_signature (0, 0, 0),
|
||||||
args_to_io_signature(args)),
|
args_to_io_signature(args)),
|
||||||
|
_center_freq(0.0f),
|
||||||
|
_freq_corr(0.0f),
|
||||||
_lo_offset(0.0f)
|
_lo_offset(0.0f)
|
||||||
{
|
{
|
||||||
size_t nchan = 1;
|
size_t nchan = 1;
|
||||||
dict_t dict = params_to_dict(args);
|
dict_t dict = params_to_dict(args);
|
||||||
|
uhd::stream_args_t stream_args("fc32", "sc16");
|
||||||
|
std::string extra_args;
|
||||||
|
std::vector <std::string> extra_list;
|
||||||
|
extra_list.push_back ("peak");
|
||||||
|
extra_list.push_back ("fullscale");
|
||||||
|
|
||||||
if (dict.count("nchan"))
|
if (dict.count("nchan"))
|
||||||
nchan = boost::lexical_cast< size_t >( dict["nchan"] );
|
nchan = boost::lexical_cast< size_t >( dict["nchan"] );
|
||||||
|
@ -58,25 +69,126 @@ uhd_source_c::uhd_source_c(const std::string &args) :
|
||||||
if ( "uhd" != entry.first &&
|
if ( "uhd" != entry.first &&
|
||||||
"nchan" != entry.first &&
|
"nchan" != entry.first &&
|
||||||
"subdev" != entry.first &&
|
"subdev" != entry.first &&
|
||||||
"lo_offset" != entry.first ) {
|
"lo_offset" != entry.first &&
|
||||||
|
"otw_format" != entry.first &&
|
||||||
|
"peak" != entry.first &&
|
||||||
|
"fullscale" != entry.first &&
|
||||||
|
"refclock" != entry.first &&
|
||||||
|
"pps" != entry.first &&
|
||||||
|
"sync" != entry.first ) {
|
||||||
arguments += entry.first + "=" + entry.second + ",";
|
arguments += entry.first + "=" + entry.second + ",";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stream_args.cpu_format = "fc32";
|
||||||
|
stream_args.otw_format = "sc16";
|
||||||
|
|
||||||
|
for (size_t chan = 0; chan < nchan; chan++)
|
||||||
|
stream_args.channels.push_back(chan); //linear mapping
|
||||||
|
|
||||||
|
if (dict.count("otw_format") )
|
||||||
|
stream_args.otw_format = dict["otw_format"];
|
||||||
|
|
||||||
|
// There's probably a more C++/Boosty way to do this.
|
||||||
|
// look for "peak" and "fullscale" args, and make up some lovely syntax
|
||||||
|
// that will be acceptable in uhd::stream_args.args
|
||||||
|
for (unsigned int q = 0; q < extra_list.size(); q++)
|
||||||
|
{
|
||||||
|
if (dict.count(extra_list[q]) )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting " + extra_list[q] + "=" + dict[extra_list[q]] + "\n";
|
||||||
|
extra_args += extra_list[q] + "=" + dict[extra_list[q]];
|
||||||
|
if (q < (extra_list.size()-1))
|
||||||
|
{
|
||||||
|
extra_args += ",";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extra_args.length() > 0)
|
||||||
|
{
|
||||||
|
// Finally stuff the args
|
||||||
|
stream_args.args = extra_args;
|
||||||
|
}
|
||||||
|
|
||||||
_src = uhd_make_usrp_source( arguments,
|
_src = uhd_make_usrp_source( arguments,
|
||||||
uhd::io_type_t::COMPLEX_FLOAT32,
|
stream_args );
|
||||||
nchan );
|
|
||||||
|
|
||||||
if (dict.count("subdev")) {
|
if (dict.count("subdev")) {
|
||||||
_src->set_subdev_spec( dict["subdev"] );
|
_src->set_subdev_spec( dict["subdev"] );
|
||||||
|
}
|
||||||
|
|
||||||
std::cerr << "-- Using subdev spec '" << _src->get_subdev_spec() << "'."
|
std::cerr << "-- Using subdev spec '" << _src->get_subdev_spec() << "'."
|
||||||
<< std::endl;
|
<< std::endl;
|
||||||
}
|
|
||||||
|
|
||||||
if (0.0 != _lo_offset)
|
if (0.0 != _lo_offset)
|
||||||
std::cerr << "-- Using lo offset of " << _lo_offset << " Hz." << std::endl;
|
std::cerr << "-- Using lo offset of " << _lo_offset << " Hz." << std::endl;
|
||||||
|
|
||||||
|
// Fargking oogly. Needs to pull ALL_MBOARDS constant out of multi_usrp:: but I can't figure out how
|
||||||
|
// So, ALL_MBOARDS is actually just size_t(~0)
|
||||||
|
size_t ALL_MBOARDS = size_t(~0);
|
||||||
|
|
||||||
|
if (dict.count("refclock") )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting refclock: " + dict["refclock"] + "\n";
|
||||||
|
_src->set_clock_source (dict["refclock"], ALL_MBOARDS);
|
||||||
|
|
||||||
|
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
|
||||||
|
|
||||||
|
uhd::sensor_value_t ref_locked = _src->get_mboard_sensor("ref_locked",0);
|
||||||
|
if (!ref_locked.to_bool())
|
||||||
|
{
|
||||||
|
std::cerr << "-- WARNING: Requested ref-clock source: " << dict["refclock"] << "\n";
|
||||||
|
std::cerr << "-- Ref-clock lock sensor indicates: UNLOCKED\n";
|
||||||
|
std::cerr << "-- You may have poorer phase noise/frequency accuracy\n";
|
||||||
|
std::cerr << "-- Phase-coherence with other devices will be poor.\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dict.count("pps") )
|
||||||
|
{
|
||||||
|
std::cerr << "-- Setting PPS source: " + dict["refclock"] + "\n";
|
||||||
|
_src->set_time_source (dict["pps"], ALL_MBOARDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set TOD across all MBOARDS to current host time
|
||||||
|
*/
|
||||||
|
if (dict.count("sync") )
|
||||||
|
{
|
||||||
|
double fracts;
|
||||||
|
long long seconds;
|
||||||
|
std::string st = boost::to_upper_copy(dict["sync"]);
|
||||||
|
|
||||||
|
get_precision_time (&seconds, &fracts);
|
||||||
|
|
||||||
|
std::cerr << "-- Setting TOD to: " << (long long)seconds << "." << (long long)fracts
|
||||||
|
<< " with method: "+st << "\n";
|
||||||
|
if (dict["sync"] == "unknown" )
|
||||||
|
{
|
||||||
|
_src->set_time_unknown_pps (uhd::time_spec_t((time_t)seconds+1));
|
||||||
|
}
|
||||||
|
else if (dict["sync"] == "next")
|
||||||
|
{
|
||||||
|
_src->set_time_next_pps (uhd::time_spec_t((time_t)seconds+1));
|
||||||
|
}
|
||||||
|
else if (dict["sync"] == "now")
|
||||||
|
{
|
||||||
|
fracts += 0.001;
|
||||||
|
if (fracts >= 1.0)
|
||||||
|
{
|
||||||
|
fracts -= 1.0;
|
||||||
|
seconds += 1LL;
|
||||||
|
}
|
||||||
|
|
||||||
|
_src->set_time_now (uhd::time_spec_t((time_t)seconds, fracts), ALL_MBOARDS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "*** Not processing sync request: unknown type: " + dict["sync"] + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for ( size_t i = 0; i < nchan; i++ )
|
for ( size_t i = 0; i < nchan; i++ )
|
||||||
connect( _src, i, self(), i );
|
connect( _src, i, self(), i );
|
||||||
}
|
}
|
||||||
|
@ -90,14 +202,18 @@ std::vector< std::string > uhd_source_c::get_devices()
|
||||||
std::vector< std::string > devices;
|
std::vector< std::string > devices;
|
||||||
|
|
||||||
uhd::device_addr_t hint;
|
uhd::device_addr_t hint;
|
||||||
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint)) {
|
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint))
|
||||||
std::string args = "uhd,subdev='A:0'," + dev.to_string();
|
{
|
||||||
|
std::string args = "uhd," + dev.to_string();
|
||||||
|
|
||||||
|
std::string type = dev.cast< std::string >("type", "usrp");
|
||||||
|
std::string name = dev.cast< std::string >("name", "");
|
||||||
|
std::string serial = dev.cast< std::string >("serial", "");
|
||||||
|
|
||||||
std::string label = "Ettus";
|
std::string label = "Ettus";
|
||||||
|
|
||||||
std::string type = dev.cast< std::string >("type", "USRP");
|
if ( "umtrx" == type )
|
||||||
std::string name = dev.cast< std::string >("name", "");
|
label = "Fairwaves";
|
||||||
std::string serial = dev.cast< std::string >("serial", "");
|
|
||||||
|
|
||||||
if (type.length()) {
|
if (type.length()) {
|
||||||
boost::to_upper(type);
|
boost::to_upper(type);
|
||||||
|
@ -105,7 +221,7 @@ std::vector< std::string > uhd_source_c::get_devices()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name.length())
|
if (name.length())
|
||||||
label += " " + name;
|
label += " (" + name + ")";
|
||||||
|
|
||||||
if (serial.length())
|
if (serial.length())
|
||||||
label += " " + serial;
|
label += " " + serial;
|
||||||
|
@ -115,9 +231,6 @@ std::vector< std::string > uhd_source_c::get_devices()
|
||||||
devices.push_back( args );
|
devices.push_back( args );
|
||||||
}
|
}
|
||||||
|
|
||||||
//devices.clear();
|
|
||||||
//devices.push_back( "uhd,type=usrp1,label='Ettus USRP'" );
|
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +249,8 @@ std::string uhd_source_c::name()
|
||||||
|
|
||||||
size_t uhd_source_c::get_num_channels()
|
size_t uhd_source_c::get_num_channels()
|
||||||
{
|
{
|
||||||
return _src->get_device()->get_rx_num_channels();
|
// return _src->get_device()->get_rx_num_channels();
|
||||||
|
return output_signature()->max_streams();
|
||||||
}
|
}
|
||||||
|
|
||||||
osmosdr::meta_range_t uhd_source_c::get_sample_rates( void )
|
osmosdr::meta_range_t uhd_source_c::get_sample_rates( void )
|
||||||
|
@ -172,13 +286,16 @@ osmosdr::freq_range_t uhd_source_c::get_freq_range( size_t chan )
|
||||||
|
|
||||||
double uhd_source_c::set_center_freq( double freq, size_t chan )
|
double uhd_source_c::set_center_freq( double freq, size_t chan )
|
||||||
{
|
{
|
||||||
//_src->set_center_freq(freq, chan);
|
#define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001))
|
||||||
|
|
||||||
|
double corr_freq = APPLY_PPM_CORR( freq, _freq_corr );
|
||||||
|
|
||||||
// advanced tuning with tune_request_t
|
// advanced tuning with tune_request_t
|
||||||
uhd::tune_request_t tune_req(freq, _lo_offset);
|
uhd::tune_request_t tune_req(corr_freq, _lo_offset);
|
||||||
|
|
||||||
_src->set_center_freq(tune_req, chan);
|
_src->set_center_freq(tune_req, chan);
|
||||||
|
|
||||||
|
_center_freq = freq;
|
||||||
|
|
||||||
return get_center_freq(chan);
|
return get_center_freq(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +306,16 @@ double uhd_source_c::get_center_freq( size_t chan )
|
||||||
|
|
||||||
double uhd_source_c::set_freq_corr( double ppm, size_t chan )
|
double uhd_source_c::set_freq_corr( double ppm, size_t chan )
|
||||||
{
|
{
|
||||||
|
_freq_corr = ppm;
|
||||||
|
|
||||||
|
set_center_freq( _center_freq );
|
||||||
|
|
||||||
return get_freq_corr( chan );
|
return get_freq_corr( chan );
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_source_c::get_freq_corr( size_t chan )
|
double uhd_source_c::get_freq_corr( size_t chan )
|
||||||
{
|
{
|
||||||
return 0; // frequency correction is not supported with UHD
|
return _freq_corr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> uhd_source_c::get_gain_names( size_t chan )
|
std::vector<std::string> uhd_source_c::get_gain_names( size_t chan )
|
||||||
|
@ -262,3 +383,95 @@ std::string uhd_source_c::get_antenna( size_t chan )
|
||||||
{
|
{
|
||||||
return _src->get_antenna(chan);
|
return _src->get_antenna(chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uhd_source_c::set_dc_offset_mode( int mode, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ( osmosdr_source_c::DCOffsetOff == mode ) {
|
||||||
|
_src->set_auto_dc_offset( false, chan );
|
||||||
|
_src->set_dc_offset( std::complex<double>(0.0, 0.0), chan ); /* uhd default */
|
||||||
|
} else if ( osmosdr_source_c::DCOffsetManual == mode ) {
|
||||||
|
_src->set_auto_dc_offset( false, chan );
|
||||||
|
} else if ( osmosdr_source_c::DCOffsetAutomatic == mode ) {
|
||||||
|
_src->set_auto_dc_offset( true, chan );
|
||||||
|
}
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uhd_source_c::set_dc_offset( const std::complex<double> &offset, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_src->set_dc_offset( offset, chan );
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uhd_source_c::set_iq_balance_mode( int mode, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
if ( osmosdr_source_c::IQBalanceOff == mode ) {
|
||||||
|
_src->set_iq_balance( std::complex<double>(0.0, 0.0), chan ); /* uhd default */
|
||||||
|
} else if ( osmosdr_source_c::IQBalanceManual == mode ) {
|
||||||
|
/* nothing to do */
|
||||||
|
} else if ( osmosdr_source_c::IQBalanceAutomatic == mode ) {
|
||||||
|
throw std::runtime_error("Automatic IQ imbalance correction not implemented");
|
||||||
|
}
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void uhd_source_c::set_iq_balance( const std::complex<double> &balance, size_t chan )
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_src->set_iq_balance( balance, chan );
|
||||||
|
} catch ( const std::exception &ex ) {
|
||||||
|
std::cerr << __FUNCTION__ << ": " << ex.what() << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double uhd_source_c::set_bandwidth( double bandwidth, size_t chan )
|
||||||
|
{
|
||||||
|
_src->set_bandwidth(bandwidth, chan);
|
||||||
|
|
||||||
|
return _src->get_bandwidth(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
double uhd_source_c::get_bandwidth( size_t chan )
|
||||||
|
{
|
||||||
|
return _src->get_bandwidth(chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
osmosdr::freq_range_t uhd_source_c::get_bandwidth_range( size_t chan )
|
||||||
|
{
|
||||||
|
osmosdr::freq_range_t bandwidths;
|
||||||
|
|
||||||
|
BOOST_FOREACH( uhd::range_t bw, _src->get_bandwidth_range(chan) )
|
||||||
|
bandwidths += osmosdr::range_t( bw.start(), bw.stop(), bw.step() );
|
||||||
|
|
||||||
|
return bandwidths;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_precision_time (long long *secs, double *fracs)
|
||||||
|
{
|
||||||
|
boost::posix_time::ptime now;
|
||||||
|
boost::posix_time::ptime epoch(boost::posix_time::from_time_t((time_t)0));
|
||||||
|
boost::posix_time::time_duration diff;
|
||||||
|
double psecs;
|
||||||
|
double fracts;
|
||||||
|
|
||||||
|
now = boost::posix_time::microsec_clock::universal_time();
|
||||||
|
|
||||||
|
diff = now - epoch;
|
||||||
|
psecs = (diff.total_milliseconds() / 1.0e3);
|
||||||
|
|
||||||
|
fracts = psecs - (long long)psecs;
|
||||||
|
fracts *= 1.0e3;
|
||||||
|
|
||||||
|
*secs = (long long)psecs;
|
||||||
|
*fracs = fracts;
|
||||||
|
}
|
||||||
|
|
|
@ -71,7 +71,19 @@ public:
|
||||||
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
|
||||||
std::string get_antenna( size_t chan = 0 );
|
std::string get_antenna( size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_dc_offset_mode( int mode, size_t chan = 0 );
|
||||||
|
void set_dc_offset( const std::complex<double> &offset, size_t chan = 0 );
|
||||||
|
|
||||||
|
void set_iq_balance_mode( int mode, size_t chan = 0 );
|
||||||
|
void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 );
|
||||||
|
|
||||||
|
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||||
|
double get_bandwidth( size_t chan = 0 );
|
||||||
|
osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0 );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
double _center_freq;
|
||||||
|
double _freq_corr;
|
||||||
double _lo_offset;
|
double _lo_offset;
|
||||||
boost::shared_ptr<uhd_usrp_source> _src;
|
boost::shared_ptr<uhd_usrp_source> _src;
|
||||||
};
|
};
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
# Include swig generation macros
|
# Include swig generation macros
|
||||||
########################################################################
|
########################################################################
|
||||||
find_package(SWIG)
|
find_package(SWIG)
|
||||||
find_package(PythonLibs)
|
find_package(PythonLibs 2)
|
||||||
if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND)
|
if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
Loading…
Reference in New Issue