Compare commits

...

96 Commits

Author SHA1 Message Date
Dimitri Stolnikov c7cb045fcb file: follow-up for commit 24d54c36 2014-05-03 18:41:43 +02:00
Jon Szymaniak b986e31e63 bladeRF: Fixed typo in handling 'stream_timeout_ms' device argument 2014-05-03 18:37:34 +02:00
Dimitri Stolnikov 24d54c369f file: expose seek function in public API 2014-05-03 18:37:05 +02:00
Dimitri Stolnikov 74b9211cdc rtl: implement methods allowing runtime to control sampling process 2014-05-03 18:30:15 +02:00
Jon Szymaniak 2ae3fdbc22 bladerf: Fixed bug in cached device cleanup
A couple issues were present in bladerf_common::close, which caused
entries in the _devs list (our "device cache") to not be removed. This
would result in a stale device handle being used upon attempting to
reopen the device.

Two issues were associated with this bug:
 - The weak_ptr expired() conditional was incorrect; the logic was
   inverted.
 - The list item removal and iterator increment was done incorrectly
   and would result in a crash after the first item was fixed.
2014-05-03 18:27:47 +02:00
Brian Padalino 79ad6f6a76 bladerf: Updated source/sink to use libbladeRF's sync interface 2014-05-03 18:27:02 +02:00
Dimitri Stolnikov 1f7e798184 source/sink: don't return void in void functions 2014-05-03 18:22:21 +02:00
Hoernchen f097a38f20 hackrf: signed samples, following commit fd77cfe8a6 2014-05-03 18:21:49 +02:00
Dimitri Stolnikov 0b76b7a21b uhd: pass through only the requested number of channels
B210 USRP appears as a 2-channel device by default. We prevent weird
application behavior by restricting the number of connected channels to
the value given via nchan= argument (1 by default).
2014-05-03 18:21:18 +02:00
Dimitri Stolnikov 79231f6173 uhd: print to stderr instead of stdout (library convention) 2014-05-03 18:17:28 +02:00
Dimitri Stolnikov e6701ca621 uhd: catch exceptions from dc offset & iq imbalance setters
since dc offset / iq imbalance is not implemented for recent USRPs this
might cause undesired behavior in GRC. As a workaround we do not pass
them to the caller but print them to the stderr.
2014-05-03 18:15:22 +02:00
Dimitri Stolnikov 376884f6a0 uhd: use universal_time() for "sync=unknown" 2014-05-03 18:04:50 +02:00
Jon Szymaniak 40f93c2ae1 bladeRF: RXVGA2 range is [0, 30]
While the RXVGA2 gain can technically go up to 60 dB, the LMS6002D
datasheet recommends it be clamped to 30dB. libbladeRF clamps to a max
of 30dB, so there's no use in setting max to 60 dB here.
2014-02-16 09:30:49 +01:00
Jon Szymaniak 1633dbaa74 bladerf: Added 'verbosity' and 'loopback' device parameters
The 'verbosity' parameter may be used to increase or suppress output from
libbladeRF. The available log levels are, in order of decreasing
verbosity are:
    verbose, debug, info, warning, critical, silent

The 'loopback' parameter may be used to put the bladeRF into one of the
supported loopback modes.  The valid modes are listed below. Their
descriptions may be found in the libbladeRF documentation:

      bb_txlpf_rxvga2, bb_txlpf_rxlpf bb_txvga1_rxvga2, bb_txvga1_rxlpf
      rf_lna1, rf_lna2, rf_lna3
2014-02-16 09:30:46 +01:00
Dimitri Stolnikov 115d4ddde0 osmosdr/rtl/miri/hackrf: use 15 buffers by default
folowing rtl-sdr commit 89f73b183f2dac9c0dd75beca4cf2f77f20c4a36

So far we had 32 * 256KB which was a bit overkill, 15 are more than
enough.

15 was chosen instead of 16 because at least on Linux there seems to be
a system-wide limit of 63 transfers (when they are 256KB large), so 4
dongles can be used on a single machine without lowering the default
transfer number.
2014-02-16 09:30:42 +01:00
Dimitri Stolnikov 333e9bbbb2 airspy: add support for AirSpy wideband receiver
Requires https://github.com/airspy/host

Usage example:

osmocom_fft -a airspy

The following named gain stages are available:

LNA: 0 to 15, step 1
MIX: 0 to 15, step 1
IF: 0 to 15, step 1

At the moment the gains are not in dB but gain indices internal to R820t
tuner.
2014-02-16 09:30:04 +01:00
Dimitri Stolnikov 084d193a29 rfspace: properly cover the absense of a serial number 2014-01-26 19:55:26 +01:00
Marcus D. Leech 3764dcb8af uhd: add options for wire-format, time-synchronization, and refclock/PPS
This patch-set adds substantial new support for UHD features relating to
wire-format, time-synchronization, and refclock/PPS.

It is intended for both sources and sinks. The get-precision-time
function I simply made static and copied into both source and sink
files. It really belongs in osmosdr utiltiies, it uses Boost to hide the
Windows-vs-Unix-like precision time "goop".
2014-01-26 17:32:33 +01:00
Brian Padalino 36a6b8fe1f bladeRF: Modifying correction calls.
Modifying correction function calls to match libbladeRF API.
2014-01-26 17:19:06 +01:00
Jon Szymaniak cd38413803 bladerf: Removed sign extension and masking of samples
This is no longer required as of FPGA v0.0.1, and has been removed to
remove some unnecessary computation on samples.
2014-01-26 17:18:45 +01:00
Jon Szymaniak 43af3a851e bladerf: Updates for libbladeRF v0.11.0 API changes 2014-01-26 17:17:19 +01:00
Jon Szymaniak 734ba42989 bladerf: Use rational sample rate functions
libbladeRF provides accessors for rational sample rates, which the
integer sample rate functions use under the hood. Therefore, there's no
need to check if the requested rate contains a fractional portion and
switch between the two sets of functions.
2014-01-26 17:17:14 +01:00
Jon Szymaniak 1e742bc186 bladerf: Added start()/stop() implementations 2014-01-26 17:16:53 +01:00
Dimitri Stolnikov b359cb71ab osmocom_fft: print the exception thrown in DC/IQ correction setters 2014-01-26 17:08:58 +01:00
Dimitri Stolnikov a832a09fd9 osmocom_siggen: add controls for DC offset and IQ imbalance correction 2014-01-26 17:08:54 +01:00
Dimitri Stolnikov aaedaa2c97 bladerf: shorten the serial number that is being shown to the user 2014-01-26 17:08:47 +01:00
Dimitri Stolnikov 58648a3124 bladerf: implement DC offset and IQ imbalance correction
This patch has been provided by Brent J.
2014-01-26 17:08:31 +01:00
Dimitri Stolnikov becc68131c bladerf: FX3 firmware loading not supported from within gr-osmosdr 2014-01-26 17:08:27 +01:00
Dimitri Stolnikov 5da9aafa2c bladerf: prepare functions to control DC offset / IQ balance (WIP) 2014-01-26 17:08:22 +01:00
Dimitri Stolnikov f42fe3d065 apps/osmocom_fft: gui to set DC offset / IQ imbalance correction mode 2014-01-26 17:08:15 +01:00
Dimitri Stolnikov 3f281d9705 rfspace: add missing header for basename() 2014-01-26 17:07:59 +01:00
Dimitri Stolnikov 7b1c5dc8a0 rfspace: add support for RFSPACE SDR-IQ and SDR-IP
Usage example:

osmocom_fft -a sdr-iq=/dev/ttyUSB0
osmocom_fft -a sdr-ip=host[:port]
osmocom_fft -a netsdr=host[:port][,nchan=2]

The following named gain stages are available:

SDR-IQ: ATT: -20 to +10 dB, in 10dB steps
SDR-IP: ATT: -30 to 0 dB, in 10dB steps

The ftdi_sio driver is being used for SDR-IQ. It creates a character
device of the form:

crw-rw---- 1 root dialout 188, 0 Dec 19 22:14 /dev/ttyUSB0

To be able to open the device without root permissions add yourself to
the "dialout" group or do a "chmod 666 /dev/ttyUSB0" after pluggin in.
2014-01-26 17:07:35 +01:00
Dimitri Stolnikov 03f9532945 device: implement "nofake" hint to exclude dummy devices from discovery
usage example:

osmosdr::device_t hint( "nofake" );
osmosdr::devices_t devs = osmosdr::device::find( hint );
2014-01-26 16:29:48 +01:00
Dimitri Stolnikov 0e6a369df7 add UmTRX to the list of supported devices
requires Fairwaves' port of the Ettus UHD library:

https://github.com/fairwaves/UHD-Fairwaves
2014-01-26 16:29:37 +01:00
Steve Markgraf 86ecf305be rtl/rtl_tcp: add 2.56e6 as 'good' sample rate
Several tests have shown that this is the
highest sample rate where no samples
are being dropped on rtl devices.

Signed-off-by: Steve Markgraf <steve@steve-m.de>
2013-12-05 00:09:16 +01:00
Dimitri Stolnikov 62127aada0 file: add missing include for boost::format 2013-12-05 00:02:37 +01:00
Dimitri Stolnikov 9e6f68b2e6 apps/osmocom_fft: recover when .step property is not given for a range 2013-12-05 00:02:30 +01:00
Dimitri Stolnikov 05e17c6417 sink/source: fix error message when unable to connect blocks internally 2013-12-05 00:02:22 +01:00
Dimitri Stolnikov 70cd91f505 netsdr: implement sample rate change in run mode 2013-12-05 00:02:16 +01:00
Steve Markgraf fa75a253c7 rtl: add R828D tuner
Signed-off-by: Steve Markgraf <steve@steve-m.de>
2013-11-04 22:25:46 +01:00
Dimitri Stolnikov b2049ba5ef netsdr: replace boost asio sockets with native bsd sockets
until we find out what's exactly interfering with fosphor when using
python based flowgraphs (osmocom_fft). c++ flowgraphs are not affected.
2013-11-04 22:25:30 +01:00
Jon Szymaniak 743dc07b43 bladerf: Don't reload the FPGA unless the force-reload param is set 2013-11-04 22:11:23 +01:00
Jon Szymaniak b7fff27300 bladerf: Moved initializations to bladerf_common.c, misc. cleanup
Common parameter handling has been moved into bladerf_common::init().

The buflen parameter is now in units of samples, not bytes. This
deviates from the other gr-osmosdr items. However, with the requirement
that buffers be in multiples of 1024 samples, this makes specifying this
parameter a bit easier. The user shouldn't need to know we're operating
on SC16Q12 values under the hood, and have to calculate accordingly.

To avoid confusion when both a bladeRF source and sink are in a flow
graph a [bladeRF source/sink] prefix has been added to output. Error
number have been replaced with bladeRF string representations of these
error values.

Firmware flashing has been removed. The bladeRF-cli or bladeRF-flash
tools are the preferred route for firmware upgrades.
2013-11-04 22:10:59 +01:00
Jon Szymaniak 892433836f bladerf: Moved no longer "common" items out of bladerf_common
The gr_complex FIFO is no longer used on the TX side, so it doesn't
really make sense to have it in bladerf_common. The associated items
have been moved into bladerf_source, and some renaming has been done in
bladerf_sink to tidy up.

Pending further performance tests of the bladerf_source, the _fifo
member (boost::circular_buffer) may need to be replaced.
2013-11-04 22:09:27 +01:00
Daniel Gröber 54f2e08dd7 bladerf: Added device caching, this enables full-duplex operation. 2013-11-04 22:09:23 +01:00
Dimitri Stolnikov 7394251bd0 netsdr: add support for RFSPACE NetSDR receiver
- implements broadcast UDP based device discovery
- prints device information (serial/versions) on startup
- reads available frequency range(s) from the device
- integrity checks of IQ stream using sequence numbers
- automatic bandpass or a wideband lowpass selectable

The following named gain stages are available:

ATT: -30 to 0 dB, in 10dB steps

Known limitations:

- setting the sample rate is possible only before the
  flowgraph has been started
2013-10-26 15:04:03 +02:00
Dimitri Stolnikov ee6e3d0bfa apps/osmocom_fft: use reasonable defaults for gain & center frequency 2013-10-26 14:51:48 +02:00
Dimitri Stolnikov 18ed13ef05 use numchan instead of nchan to prevent ambiguity...
..when specifying total number of channels offered by the block
2013-10-25 23:50:49 +02:00
Dimitri Stolnikov 6252b4144a hackrf: implement device discovery
Unfortunately libhackrf still doesn't offer a way to enumerate devices
*or* to open a specific device by index or it's serial number. Thus we
have implemented a rather hack-ish way to detect the presence of a
device by trying to open it and closing right after that.
2013-10-25 23:49:24 +02:00
Dimitri Stolnikov e394dd3922 uhd: improve device discovery 2013-10-25 23:46:39 +02:00
Dimitri Stolnikov 0923bcb687 remove unused variables 2013-10-25 23:46:34 +02:00
Dimitri Stolnikov 96b5e4388f file: allow changing the sample rate when throttle=true
this is dangerous from signal processing perspective and should be used
with caution.
2013-10-20 18:50:27 +02:00
Dimitri Stolnikov 596790e8c1 cmake: fix flag passing to Clang compiler 2013-10-19 16:38:05 +02:00
Dimitri Stolnikov 6fa0815be0 apps: catch corner cases for file= mode in osmocom_fft
verified for gqrx and sdrangerlove as well:

file=/tmp/lte.cfile,rate=40e6,freq=1815e6,repeat=true,throttle=true
2013-10-18 19:49:12 +02:00
Dimitri Stolnikov c7dcddc3cd bladerf: use newstyle version getters 2013-10-04 18:32:40 +02:00
Dimitri Stolnikov a81016d230 bladerf: make source less picky wrt rf gain 2013-10-03 14:50:24 +02:00
Dimitri Stolnikov 884ee47605 source/sink: initialize sample rate cache variable
Thanks to Jiří Pinkava for bringing this up.
2013-10-03 14:50:16 +02:00
Dimitri Stolnikov 6187f182d5 source/sink: fix automatic bandwidth selection 2013-10-03 14:45:12 +02:00
Dimitri Stolnikov 2d644d2eae rtl: don't try to parse empty device index values 2013-09-28 13:59:23 +02:00
Dimitri Stolnikov 2dfc73351f apps: filter bandwidth follows sample rate in osmocom_fft 2013-09-28 13:59:18 +02:00
Dimitri Stolnikov 89db739999 updated header documentation for bandwidth setter 2013-09-28 13:59:09 +02:00
Dimitri Stolnikov 681e3b7d4a bladerf: implement automatic bandwidth selection 2013-09-28 13:59:03 +02:00
Dimitri Stolnikov 5d5dc04371 hackrf: enable AMP gain stage by default 2013-09-28 13:58:57 +02:00
Dimitri Stolnikov 911f916b3b hackrf: don't set automatic bandwidth on samplerate change
automatic bandwidth selection may be triggered by calling
set_bandwidth(0) after changing the sample rate
2013-09-28 13:58:50 +02:00
Jon Szymaniak a0b534d118 bladeRF: Refactored buffering scheme in sink
Removed the use of an intermediate sample FIFO in the sink
implementation. Note the the FIFO has not been moved out of
bladerf_common --> bladerf_source_c in this commit.

work() now handles converting samples from complex to SC16_Q12, and filling
"transmit-ready" buffers. The callbacks are now only responsible for
marking the provided buffer free, and returning the next buffer.

It appears that a small deadlock issues remains in this changest, which
can be induced by:
 1: Using a small sample rate (160Khz)
 2: Switching back and forth between sinusoid <-> GSM burst

 In this case, it appears that work() is blocked waiting for a buffer to
 become free. More investigation here is required...
2013-09-28 00:42:46 +02:00
Dimitri Stolnikov 1c9a793a70 bladerf: migration to async api (WIP)
receive works, transmit locks up in work() after few seconds
2013-09-28 00:37:58 +02:00
Frederik M.J. Vestre 2e9828f120 Support setting external sampling to bladerf 2013-08-29 23:38:20 +02:00
Dimitri Stolnikov c16a562ddc bladerf: follow recent API changes
tested against aea04c5f119288370166ece05166a8a4157da6fa
2013-08-27 22:42:24 +02:00
Dimitri Stolnikov d0a043ace3 cmake: use *_INCLUDE_DIRS in modules 2013-08-13 20:20:17 +02:00
Dimitri Stolnikov 11841ccb29 bladerf: update common sample rate values 2013-07-28 20:04:27 +02:00
Dimitri Stolnikov 41bb9a0ef9 bladerf: migrate to new api to enable/disable TX/RX modules
TX support has been verified with osmocom_siggen and fpga image from git
f6c6a3abcb22d2794946e5adbc556805a73788a3
2013-07-28 20:04:17 +02:00
Dimitri Stolnikov 65e34f6863 bladerf: add support for nuand LLC bladeRF (WIP)
This is based on the original work (https://github.com/Nuand/gr-osmosdr)
done by folks at nuand LLC for the gr3.6 branch of gr-osmosdr.

The following modifications have been done in this commit:

* port to gr-osmosdr master codebase (gr3.7)
* moved shared properties to bladerf_common
* added & verified IF filter bandwidth setters
* set LMS6002D registers with values taken from FAQ 5.27
* print device information (serial/versions) on startup
* added fpga= and fw= device arguments to program MCU/FPGA
* added bladerf=# dev. arg. to select a specific bladeRF
* grc gain field controls RF path VGA for RX/TX
* grc BB gain field controls BB path VGA for RX/TX

Usage example:

osmocom_fft -a "bladerf,fpga=/tmp/hostedx115.rbf"

The following RX named gain stages are available:

LNA: 0 to 6 dB, in 3dB steps
VGA1: 5 to 30 dB, in 1dB steps; nonlinear mapping done inside the lib
VGA2: 0 to 60 dB, in 3dB steps; not recommended to be used above 30dB

The following TX named gain stages are available:

VGA1: -35 to -4 dB, in 1dB steps, BB side
VGA2: 0 to 25 dB, in 1dB steps, RF side

Thanks a lot to the team of nuand LLC for this major contribution.
2013-07-28 19:57:45 +02:00
Dimitri Stolnikov 7058fd6610 fcd: add support for FUNcube Dongle Pro+
The "3.6" branch of the gnuradio block https://github.com/dl1ksv/gr-

Available named gains:

Dongle Classic:

LNA: -5 to 30 dB, in 2.5 dB steps
MIX: 4 or 12 dB

Dongle Pro+:

LNA: 0 or 1, meaning off/on only. no information about real values.
MIX: 0 or 1, meaning off/on only. no information about real values.
BB: 0 to 59 dB, in 1 dB steps

This patch also introduces optional "device" and "type" arguments which
allow to override the values automatically picked by gr-osmosdr:

osmocom_fft -a "fcd,device=hw:2,type=2"

The "device" argument overrides the audio device used by the underlying
driver to access the dongle's IQ sample stream.

The "type" argument selects the dongle type, 1 for Classic, 2 for Pro+.

Thanks to Alexey Bazhin for the initial patch and Volker Schroer for
testing.
2013-07-28 19:16:28 +02:00
Dimitri Stolnikov 6e2a2023d5 cmake: require Python v2 for build configuration
based on a gnuradio patch by Jiri Pinkava
http://gnuradio.org/cgit/gnuradio.git/commit/?id=8144572532b8bfa37af01e1264a334d2a898ea8d
2013-07-28 17:49:10 +02:00
Dimitri Stolnikov 2d02ae9b83 rtl: remove residue DC component introduced by RTL chip
received from Juha Vierinen:

A student here noticed that there is dc bias even with the rafael tuner.
We looked into this issue and found that using 127.4f instead of 127.5f
removes this bias. I assume this is associated with a bug in the digital
downconversion of the RTL chip. This change fixes the problem.
2013-07-28 17:48:42 +02:00
Dimitri Stolnikov ffd2bbf424 update version to 0.0.3git 2013-06-12 20:29:26 +02:00
Dimitri Stolnikov 7b11f872c4 update version to 0.0.2 2013-06-12 20:18:51 +02:00
Dimitri Stolnikov e42752826c uhd: implement frequency correction 2013-06-10 21:21:14 +02:00
Dimitri Stolnikov d25666b594 apps: allow float frequency correction values 2013-06-09 23:36:58 +02:00
Dimitri Stolnikov 6f04a9b03a uhd: implement dc correction 2013-06-09 23:13:12 +02:00
Dimitri Stolnikov e1f4c24232 hackrf: select narrower filters in auto bw mode to prevent aliasing 2013-06-09 13:11:32 +02:00
Dimitri Stolnikov ed627cea9c hackrf: use new sample rate function introduced in libhackrf b892bc34ad 2013-06-09 09:03:54 +02:00
Dimitri Stolnikov f70550fe40 uhd: implement bandwidth controls 2013-06-08 22:16:05 +02:00
Dimitri Stolnikov 5a4033869f cmake: update url to documentation page 2013-06-08 17:39:56 +02:00
Dimitri Stolnikov faa1f44550 return cached values in setters immediately if same as requested 2013-06-08 17:13:16 +02:00
Dimitri Stolnikov 1822e88148 apps: enable bandwidth control for osmocom_fft and osmocom_siggen 2013-06-08 17:11:53 +02:00
Dimitri Stolnikov 2cd4126288 hackrf: add fractional sample rate support introduced in libhackrf
d9c46cbdac
2013-06-07 22:37:29 +02:00
Dimitri Stolnikov b59b0080f5 hackrf: add human readable error prints to all libhackrf funcs 2013-06-07 22:06:38 +02:00
Dimitri Stolnikov eaec4c5acc apps: check for sample rate availability 2013-06-05 19:58:16 +02:00
Dimitri Stolnikov ba5c6c8da6 hackrf: set the default sample rate to 10M
this prevents a regression with early firmware who did not have 8M rate
but 5M instead. they both had 10M, so we use this as default.
2013-06-05 19:21:13 +02:00
Dimitri Stolnikov 17c2bda9f8 hackrf: disable start/stop as it has caused lock-ups 2013-06-02 16:17:27 +02:00
Dimitri Stolnikov 9714f9cd74 change OSMOCOM to lowercase like in master 2013-05-30 17:21:49 +02:00
Dimitri Stolnikov 0e6b6f9ec9 update revision to 0.0.2git
for next maintenance release 0.0.2 of gr3.6 block series
2013-05-30 17:09:41 +02:00
Dimitri Stolnikov 9cbe22f23d hackrf: change out-of-spec rate 5e6 to 8e6 default 2013-05-30 01:00:45 +02:00
Dimitri Stolnikov 6a70539872 hackrf: update to libhackrf gain API introduced in b5f275abc5 2013-05-30 00:47:32 +02:00
Dimitri Stolnikov fed7cfd368 update revision to 0.0.1
this is the legacy version for gnuradio 3.6
2013-05-29 22:38:14 +02:00
67 changed files with 6664 additions and 598 deletions

View File

@ -1,3 +1,4 @@
Dimitri Stolnikov <horiz0n@gmx.net>
Steve Markgraf <steve@steve-m.de>
Hoernchen <la@tfc-server.de>
Nuand LLC folks

View File

@ -36,7 +36,7 @@ set(CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE} CACHE STRING "")
# Set the version information here
set(VERSION_INFO_MAJOR_VERSION 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)
include(GrVersion) #setup version info
@ -56,10 +56,14 @@ ENDIF()
message(FATAL_ERROR "Option ${USE_SIMD} not supported, valid entries are ${USE_SIMD_VALUES}")
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(-Wextra)
ADD_DEFINITIONS(-Wno-unused)
ADD_DEFINITIONS(-Wno-unused-parameter)
ADD_DEFINITIONS(-Wsign-compare)
#ADD_DEFINITIONS(-Wconversion)
#ADD_DEFINITIONS(-pedantic)
@ -86,7 +90,7 @@ ELSEIF(MSVC)
ADD_DEFINITIONS(/arch:AVX)
ADD_DEFINITIONS(-DUSE_AVX)
ENDIF()
ENDIF(CMAKE_COMPILER_IS_GNUCXX)
ENDIF()
########################################################################
# Setup boost
@ -149,10 +153,13 @@ find_package(GnuradioIQBalance)
find_package(UHD)
find_package(GnuradioUHD)
find_package(GnuradioFCD)
find_package(GnuradioFCDPP)
find_package(LibOsmoSDR)
find_package(LibRTLSDR)
find_package(LibMiriSDR)
find_package(LibHackRF)
find_package(LibAIRSPY)
find_package(LibbladeRF)
find_package(Doxygen)
if(NOT GRUEL_FOUND)
@ -199,7 +206,7 @@ add_custom_target(uninstall
########################################################################
# Enable python component
########################################################################
find_package(PythonLibs)
find_package(PythonLibs 2)
find_package(SWIG)
if(SWIG_FOUND)

17
README
View File

@ -1,13 +1,18 @@
While primarily being developed for the OsmoSDR hardware, this block as well supports:
* FunCube Dongle through libgnuradio-fcd
* OSMOCOM OsmoSDR Devices through libosmosdr
* Great Scott Gadgets HackRF through libhackrf
* Ettus USRP Devices through Ettus UHD library
* FUNcube Dongle through libgnuradio-fcd
* FUNcube Dongle Pro+ through gr-fcdproplus
* sysmocom OsmoSDR Devices through libosmosdr
* RTL2832U based DVB-T dongles through librtlsdr
* RTL-TCP spectrum server (see librtlsdr project)
* 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.
@ -32,4 +37,4 @@ make
sudo make install
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.

View File

@ -66,17 +66,21 @@ class app_top_block(stdgui2.std_top_block, pubsub):
help="Set sample rate (bandwidth), minimum by default")
parser.add_option("-f", "--center-freq", type="eng_float", default=None,
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)")
parser.add_option("-g", "--gain", type="eng_float", default=None,
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,
help="Enable waterfall display")
parser.add_option("-S", "--oscilloscope", action="store_true", default=False,
help="Enable oscilloscope display")
parser.add_option("", "--avg-alpha", type="eng_float", default=1e-1,
help="Set fftsink averaging factor, default=[%default]")
parser.add_option ("", "--averaging", action="store_true", default=False,
parser.add_option("", "--averaging", action="store_true", default=False,
help="Enable fftsink averaging, default=[%default]")
parser.add_option("", "--ref-scale", type="eng_float", default=1.0,
help="Set dBFS=0dB input value, default=[%default]")
@ -97,6 +101,12 @@ class app_top_block(stdgui2.std_top_block, pubsub):
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
if(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()
if options.gain is None:
# if no gain was specified, use the mid-point in dB
r = self.src.get_gain_range()
options.gain = float(r.start()+r.stop())/2
gain = self.src.get_gain()
if gain is None:
# if no gain was specified, use the mid-point in dB
r = self.src.get_gain_range()
try: # empty gain range returned in file= mode
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 no freq was specified, use the mid-point in Hz
r = self.src.get_freq_range()
options.center_freq = float(r.start()+r.stop())/2
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
r = self.src.get_freq_range()
options.center_freq = float(r.start()+r.stop())/2
input_rate = self.src.set_sample_rate(options.samp_rate)
self.src.set_bandwidth(input_rate)
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(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[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
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(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
for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY):
print key, "=", self[key]
#for key in (SAMP_RATE_KEY, BWIDTH_KEY, CENTER_FREQ_KEY, FREQ_CORR_KEY):
#print key, "=", self[key]
#self[key] = self[key]
if options.waterfall:
self.scope = \
waterfallsink2.waterfallsrc_c (panel, fft_size=options.fft_size,
sample_rate=input_rate)
self.scope = waterfallsink2.waterfall_sink_c (panel,
fft_size=options.fft_size,
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))
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))
else:
self.scope = fftsink2.fft_sink_c (panel,
@ -172,21 +212,27 @@ class app_top_block(stdgui2.std_top_block, pubsub):
average=options.averaging,
avg_alpha=options.avg_alpha,
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.connect(self.src, self.scope)
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
if not(self.set_freq(options.center_freq)):
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):
self.frame.GetStatusBar().SetStatusText(msg, 0)
@ -231,39 +277,46 @@ class app_top_block(stdgui2.std_top_block, pubsub):
)
freq_hbox.AddSpacer(5)
forms.slider(
parent=self.panel, sizer=freq_hbox,
proportion=3,
ps=self,
key=CENTER_FREQ_KEY,
minimum=self[FREQ_RANGE_KEY].start(),
maximum=self[FREQ_RANGE_KEY].stop(),
num_steps=101,
)
freq_hbox.AddSpacer(3)
try: # range.start() == range.stop() in file= mode
corr_hbox.AddSpacer(3)
forms.text_box(
parent=self.panel, sizer=corr_hbox,
label='Freq. Correction (ppm)',
proportion=1,
converter=forms.int_converter(),
ps=self,
key=FREQ_CORR_KEY,
)
corr_hbox.AddSpacer(5)
forms.slider(
parent=self.panel, sizer=freq_hbox,
proportion=3,
ps=self,
key=CENTER_FREQ_KEY,
minimum=self[FREQ_RANGE_KEY].start(),
maximum=self[FREQ_RANGE_KEY].stop(),
num_steps=1000,
)
freq_hbox.AddSpacer(3)
forms.slider(
parent=self.panel, sizer=corr_hbox,
proportion=3,
ps=self,
key=FREQ_CORR_KEY,
minimum=-100,
maximum=+100,
num_steps=201,
step_size=1,
)
corr_hbox.AddSpacer(3)
except AssertionError:
pass
if self[FREQ_CORR_KEY] != None: # show frequency correction scrollbar
corr_hbox.AddSpacer(3)
forms.text_box(
parent=self.panel, sizer=corr_hbox,
label='Freq. Correction (ppm)',
proportion=1,
converter=forms.float_converter(),
ps=self,
key=FREQ_CORR_KEY,
)
corr_hbox.AddSpacer(5)
forms.slider(
parent=self.panel, sizer=corr_hbox,
proportion=3,
ps=self,
key=FREQ_CORR_KEY,
minimum=-100,
maximum=+100,
num_steps=2010,
step_size=0.1,
)
corr_hbox.AddSpacer(3)
##################################################
# Gain controls
@ -306,7 +359,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
key=GAIN_KEY(gain_name),
minimum=range.start(),
maximum=range.stop(),
step_size=range.step(),
step_size=range.step() or (range.stop() - range.start())/10,
)
gain_hbox.AddSpacer(3)
@ -317,8 +370,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
bw_range = self[BWIDTH_RANGE_KEY]
#print bw_range.to_pp_string()
#if bw_range.start() < bw_range.stop():
if 0:
if bw_range.start() < bw_range.stop():
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
label="Bandwidth",
orient=wx.VERTICAL,
@ -349,7 +401,7 @@ class app_top_block(stdgui2.std_top_block, pubsub):
key=BWIDTH_KEY,
minimum=bw_range.start(),
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)
@ -397,11 +449,227 @@ class app_top_block(stdgui2.std_top_block, pubsub):
#)
#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):
samp_rate = self.src.set_sample_rate(samp_rate)
self.scope.set_sample_rate(samp_rate)
if self._verbose:
print "Set sample rate to:", samp_rate
try:
self[BWIDTH_KEY] = self.set_bandwidth(samp_rate)
except RuntimeError:
pass
return samp_rate
def get_gain_names(self):
@ -421,9 +689,18 @@ class app_top_block(stdgui2.std_top_block, pubsub):
print "Set " + name + " gain to:", gain
def set_bandwidth(self, bw):
bw = self.src.set_bandwidth(bw)
if self._verbose:
print "Set bandwidth to:", 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:
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):
if freq is None:
@ -458,34 +735,8 @@ class app_top_block(stdgui2.std_top_block, pubsub):
if self._verbose:
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 ():
app = stdgui2.stdapp(app_top_block, "OSMOCOM Spectrum Browser", nstatus=1)
app = stdgui2.stdapp(app_top_block, "osmocom Spectrum Browser", nstatus=1)
app.MainLoop()
if __name__ == '__main__':

View File

@ -179,7 +179,7 @@ class app_gui(pubsub):
parent=self.panel, sizer=corr_hbox,
label='Freq. Correction (ppm)',
proportion=1,
converter=forms.int_converter(),
converter=forms.float_converter(),
ps=self.tb,
key=osmocom_siggen.FREQ_CORR_KEY,
)
@ -192,8 +192,8 @@ class app_gui(pubsub):
key=osmocom_siggen.FREQ_CORR_KEY,
minimum=-100,
maximum=+100,
num_steps=201,
step_size=1,
num_steps=2010,
step_size=0.1,
)
corr_hbox.AddSpacer(3)
@ -275,8 +275,7 @@ class app_gui(pubsub):
bw_range = self.tb[osmocom_siggen.BWIDTH_RANGE_KEY]
#print bw_range.to_pp_string()
#if bw_range.start() < bw_range.stop():
if 0:
if bw_range.start() < bw_range.stop():
bwidth_vbox = forms.static_box_sizer(parent=self.panel,
label="Bandwidth",
orient=wx.VERTICAL,
@ -314,6 +313,130 @@ class app_gui(pubsub):
except RuntimeError:
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
##################################################
@ -347,7 +470,7 @@ def main():
gui=app_gui, # User interface class
options=options, # Command line options
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
start=True, # Whether to start flowgraph
realtime=True) # Whether to set realtime priority

View File

@ -33,6 +33,10 @@ WAVEFORM2_FREQ_KEY = 'waveform2_freq'
FREQ_RANGE_KEY = 'freq_range'
GAIN_RANGE_KEY = lambda x: 'gain_range:'+x
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'
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[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
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(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
for key in (SAMP_RATE_KEY, GAIN_KEY, BWIDTH_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):
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:
options.samp_rate = self._sink.get_sample_rates().start()
@ -226,9 +247,34 @@ class top_block(gr.top_block, pubsub):
print "Set " + name + " gain to:", gain
def set_bandwidth(self, bw):
bw = self._sink.set_bandwidth(bw)
if self._verbose:
print "Set bandwidth to:", 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:
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):
if freq is None:

View File

@ -149,6 +149,12 @@ class my_top_block(gr.top_block):
# build graph
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
if(options.antenna):
self.u.set_antenna(options.antenna, 0)

View File

@ -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)

View File

@ -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)

View File

@ -1,6 +1,6 @@
if(NOT LIBMIRISDR_FOUND)
pkg_check_modules (LIBMIRISDR_PKG libmirisdr)
find_path(LIBMIRISDR_INCLUDE_DIR NAMES mirisdr.h
find_path(LIBMIRISDR_INCLUDE_DIRS NAMES mirisdr.h
PATHS
${LIBMIRISDR_PKG_INCLUDE_DIRS}
/usr/include
@ -14,14 +14,14 @@ if(NOT LIBMIRISDR_FOUND)
/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")
message(STATUS "Found libmirisdr: ${LIBMIRISDR_INCLUDE_DIR}, ${LIBMIRISDR_LIBRARIES}")
else(LIBMIRISDR_INCLUDE_DIR AND LIBMIRISDR_LIBRARIES)
message(STATUS "Found libmirisdr: ${LIBMIRISDR_INCLUDE_DIRS}, ${LIBMIRISDR_LIBRARIES}")
else(LIBMIRISDR_INCLUDE_DIRS AND LIBMIRISDR_LIBRARIES)
set(LIBMIRISDR_FOUND FALSE CACHE INTERNAL "libmirisdr 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)

View File

@ -1,6 +1,6 @@
if(NOT LIBOSMOSDR_FOUND)
pkg_check_modules (LIBOSMOSDR_PKG libosmosdr)
find_path(LIBOSMOSDR_INCLUDE_DIR NAMES osmosdr.h
find_path(LIBOSMOSDR_INCLUDE_DIRS NAMES osmosdr.h
PATHS
${LIBOSMOSDR_PKG_INCLUDE_DIRS}
/usr/include
@ -14,14 +14,14 @@ if(NOT LIBOSMOSDR_FOUND)
/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")
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIR}, ${LIBOSMOSDR_LIBRARIES}")
else(LIBOSMOSDR_INCLUDE_DIR AND LIBOSMOSDR_LIBRARIES)
message(STATUS "Found libosmosdr: ${LIBOSMOSDR_INCLUDE_DIRS}, ${LIBOSMOSDR_LIBRARIES}")
else(LIBOSMOSDR_INCLUDE_DIRS AND LIBOSMOSDR_LIBRARIES)
set(LIBOSMOSDR_FOUND FALSE CACHE INTERNAL "libosmosdr 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)

View File

@ -1,6 +1,6 @@
if(NOT LIBRTLSDR_FOUND)
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
${LIBRTLSDR_PKG_INCLUDE_DIRS}
/usr/include
@ -14,14 +14,14 @@ if(NOT LIBRTLSDR_FOUND)
/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")
message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIR}, ${LIBRTLSDR_LIBRARIES}")
else(LIBRTLSDR_INCLUDE_DIR AND LIBRTLSDR_LIBRARIES)
message(STATUS "Found librtlsdr: ${LIBRTLSDR_INCLUDE_DIRS}, ${LIBRTLSDR_LIBRARIES}")
else(LIBRTLSDR_INCLUDE_DIRS AND LIBRTLSDR_LIBRARIES)
set(LIBRTLSDR_FOUND FALSE CACHE INTERNAL "librtlsdr 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)

View File

@ -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)

View File

@ -36,11 +36,11 @@ if(PYTHON_EXECUTABLE)
else(PYTHON_EXECUTABLE)
#use the built-in find script
find_package(PythonInterp)
find_package(PythonInterp 2)
#and if that fails use the find program routine
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)
set(PYTHONINTERP_FOUND TRUE)
endif(PYTHON_EXECUTABLE)

View File

@ -115,7 +115,7 @@ macro(GR_SWIG_MAKE name)
endif()
#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_DIRS})
list(APPEND GR_SWIG_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR})

View File

@ -5,7 +5,7 @@ includedir=${prefix}/@GR_INCLUDE_DIR@
Name: @CPACK_PACKAGE_NAME@
Description: @CPACK_PACKAGE_DESCRIPTION_SUMMARY@
URL: http://sdr.osmocom.org/trac/
URL: http://sdr.osmocom.org/trac/wiki/GrOsmoSDR
Version: @CPACK_PACKAGE_VERSION@
Requires: gnuradio-core
Requires.private: @GR_OSMOSDR_PC_REQUIRES@

View File

@ -26,14 +26,17 @@ MAIN_TMPL = """\
<category>$($sourk.title())s</category>
<throttle>1</throttle>
<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)
#for $n in range($max_nchan)
\#if \$nchan() > $n
self.\$(id).set_center_freq(\$freq$(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_gain_mode(\$gain_mode$(n), $n)
#end if
self.\$(id).set_gain(\$gain$(n), $n)
self.\$(id).set_if_gain(\$if_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)
<callback>set_center_freq(\$freq$(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_gain_mode(\$gain_mode$(n), $n)</callback>
#end if
<callback>set_gain(\$gain$(n), $n)</callback>
<callback>set_if_gain(\$if_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>
</$sourk>
<doc>
The OSMOCOM block:
The osmocom $sourk block:
While primarily being developed for the OsmoSDR hardware, this block as well supports:
* FunCube Dongle through libgnuradio-fcd
* OSMOCOM OsmoSDR Devices through libosmosdr
* Great Scott Gadgets HackRF through libhackrf
* Ettus USRP Devices through Ettus UHD library
#if $sourk == 'source':
* FUNcube Dongle through libgnuradio-fcd
* FUNcube Dongle Pro+ through gr-fcdproplus
* sysmocom OsmoSDR Devices through libosmosdr
* RTL2832U based DVB-T dongles through librtlsdr
* RTL-TCP spectrum server (see librtlsdr project)
* 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:
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.
Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space.
Source Mode:
fcd=0
hackrf=0[,buffers=32]
#if $sourk == 'source':
fcd=0[,device=hw:2][,type=2]
miri=0[,buffers=32] ...
rtl=serial_number ...
rtl=0[,rtl_xtal=28.8e6][,tuner_xtal=28.8e6] ...
rtl=1[,buffers=32][,buflen=N*512] ...
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] ...
uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ...
osmosdr=0[,buffers=32][,buflen=N*512] ...
file='/path/to/your file',rate=1e6[,freq=100e6][,repeat=true][,throttle=true] ...
Sink Mode:
netsdr=127.0.0.1[:50000][,nchan=2]
sdr-ip=127.0.0.1[:50000]
sdr-iq=/dev/ttyUSB0
airspy=0
#end if
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\\\\''] ...
Num Channels:
@ -160,6 +175,15 @@ The center frequency is the frequency the RF chain is tuned to.
Freq. Corr.:
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:
Controls the behavior of software IQ imbalance corrrection.
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.
Currently, only RTL-SDR devices support automatic gain mode.
#end if
RF Gain:
Overall RF gain of the receiving device.
Overall RF gain of the device.
IF Gain:
Overall intermediate frequency gain of the receiving 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.
Overall intermediate frequency gain of the device.
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:
Overall baseband gain of the receiving device.
This setting has only effect for HackRF Jawbreaker. Observations lead to a reasonable gain range from 15 to 30dB.
Overall baseband gain of the device.
This setting is available for HackRF Jawbreaker in receive mode. Observations lead to a reasonable gain range from 15 to 30dB.
Antenna:
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.
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/rtl-sdr
http://sdr.osmocom.org/trac/
</doc>
</block>
"""
@ -214,6 +239,26 @@ PARAMS_TMPL = """
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</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>
<name>Ch$(n): IQ Balance Mode</name>
<key>iq_balance_mode$(n)</key>
@ -248,6 +293,7 @@ PARAMS_TMPL = """
<key>1</key>
</option>
</param>
#end if
<param>
<name>Ch$(n): RF Gain (dB)</name>
<key>gain$(n)</key>
@ -318,7 +364,7 @@ if __name__ == '__main__':
title = 'RTL-SDR'
prefix = 'rtlsdr'
elif tail.startswith('osmosdr'):
title = 'OSMOCOM'
title = 'osmocom'
prefix = 'osmosdr'
else: raise Exception, 'file %s has wrong syntax!'%tail
@ -330,7 +376,7 @@ if __name__ == '__main__':
dir = 'in'
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,
max_nchan=max_num_channels,
params=params,

View File

@ -101,6 +101,9 @@ namespace osmosdr {
* The device hint should be used to narrow down the search
* 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
* \return a vector of logical devices for all radios on the system
*/

View File

@ -240,33 +240,27 @@ public:
*/
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
*/
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.
*
* \param correction the complex correction value
* \param balance the complex correction value
* \param chan the channel index 0 to N-1
*/
virtual void set_iq_balance( const std::complex<double> &correction,
size_t chan = 0 ) = 0;
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) = 0;
/*!
* 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
* \return the actual filter bandwidth in Hz
*/

View File

@ -63,6 +63,15 @@ public:
*/
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.
* \return a range of rates in Sps
@ -240,6 +249,36 @@ public:
*/
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 {
IQBalanceOff = 0,
IQBalanceManual,
@ -258,15 +297,14 @@ public:
* Set the RX frontend IQ balance correction.
* 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
*/
virtual void set_iq_balance( const std::complex<double> &correction,
size_t chan = 0 ) = 0;
virtual void set_iq_balance( const std::complex<double> &balance, size_t chan = 0 ) = 0;
/*!
* 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
* \return the actual filter bandwidth in Hz
*/

View File

@ -69,10 +69,17 @@ endif(ENABLE_OSMOSDR)
########################################################################
# 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)
GR_INCLUDE_SUBDIRECTORY(fcd)
add_definitions(-DHAVE_FCD=1)
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
@ -122,6 +129,30 @@ if(ENABLE_HACKRF)
GR_INCLUDE_SUBDIRECTORY(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
########################################################################

37
lib/airspy/CMakeLists.txt Normal file
View 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})

View File

@ -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;
}

View File

@ -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 */

View File

@ -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})

View File

@ -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;
}

View File

@ -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

View File

@ -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();
}

View File

@ -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 */

View File

@ -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();
}

View File

@ -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 */

View File

@ -12,5 +12,8 @@
#cmakedefine ENABLE_UHD
#cmakedefine ENABLE_MIRI
#cmakedefine ENABLE_HACKRF
#cmakedefine ENABLE_BLADERF
#cmakedefine ENABLE_RFSPACE
#cmakedefine ENABLE_AIRSPY
#endif // CONFIG_H_IN

View File

@ -21,10 +21,15 @@
# This file included, use CMake directory variables
########################################################################
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${GNURADIO_FCD_INCLUDE_DIRS}
)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
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
${CMAKE_CURRENT_SOURCE_DIR}/fcd_source.cc
@ -34,5 +39,11 @@ set(fcd_srcs
# Append gnuradio-osmosdr library sources
########################################################################
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)

View File

@ -1,6 +1,6 @@
/* -*- 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
* 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
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::ifstream cards( "/proc/asound/cards" );
@ -55,15 +62,23 @@ static std::vector< std::string > _get_devices()
{
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;
std::istringstream( line ) >> 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) :
gr_hier_block2("fcd_source",
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;
unsigned int dev_index = 0;
@ -84,18 +100,70 @@ fcd_source::fcd_source(const std::string &args) :
dict_t dict = params_to_dict(args);
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() )
dev_name = devices[dev_index];
else
throw std::runtime_error("No FunCube Dongle found.");
{
if ( FUNCUBE_UNKNOWN == _type )
_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()
@ -107,9 +175,15 @@ std::vector< std::string > fcd_source::get_devices()
int id = 0;
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++ );
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 );
}
@ -118,7 +192,12 @@ std::vector< std::string > fcd_source::get_devices()
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 )
@ -142,19 +221,35 @@ double fcd_source::set_sample_rate( double rate )
double fcd_source::get_sample_rate( void )
{
return 96e3;
if ( FUNCUBE_V1 == _type )
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 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 )
{
_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;
@ -168,7 +263,15 @@ double fcd_source::get_center_freq( 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;
@ -185,44 +288,131 @@ std::vector<std::string> fcd_source::get_gain_names( size_t chan )
std::vector< std::string > names;
names += "LNA";
names += "MIX";
if ( FUNCUBE_V2 == _type )
names += "BB";
return names;
}
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 )
{
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 )
{
_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);
}
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 )
{
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 )
{
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 )

View File

@ -1,6 +1,6 @@
/* -*- 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
* it under the terms of the GNU General Public License as published by
@ -22,7 +22,13 @@
#include <gr_hier_block2.h>
#ifdef HAVE_FCD
#include <fcd_source_c.h>
#endif
#ifdef HAVE_FCDPP
#include <fcdproplus/fcdproplus.h>
#endif
#include "osmosdr_src_iface.h"
@ -44,6 +50,12 @@ private:
public:
~fcd_source();
enum dongle_type {
FUNCUBE_UNKNOWN,
FUNCUBE_V1,
FUNCUBE_V2
};
static std::vector< std::string > get_devices();
std::string name();
@ -73,9 +85,15 @@ public:
std::string get_antenna( size_t chan = 0 );
private:
fcd_source_c_sptr _src;
double _gain, _freq;
int32_t _correct;
dongle_type _type;
#ifdef HAVE_FCD
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

View File

@ -23,10 +23,9 @@
#include <sstream>
#include <boost/assign.hpp>
#include <boost/format.hpp>
#include <gr_io_signature.h>
#include <gr_file_source.h>
#include <gr_throttle.h>
#include "file_source_c.h"
@ -73,20 +72,22 @@ file_source_c::file_source_c(const std::string &args) :
if (_freq < 0)
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.");
gr_file_source_sptr src = gr_make_file_source( sizeof(gr_complex),
filename.c_str(),
repeat );
_file_rate = _rate;
_source = gr_make_file_source( sizeof(gr_complex),
filename.c_str(),
repeat );
_throttle = gr_make_throttle( sizeof(gr_complex), _file_rate );
if (throttle) {
gr_throttle::sptr throttle = gr_make_throttle( sizeof(gr_complex), _rate );
connect( src, 0, throttle, 0 );
connect( throttle, 0, self(), 0 );
connect( _source, 0, _throttle, 0 );
connect( _throttle, 0, self(), 0 );
} 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";
}
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::string args = "file='/path/to/your/file',rate=1e6,freq=100e6,repeat=true,throttle=true";
args += ",label='Complex Sampled (IQ) File'";
devices.push_back( args );
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'";
devices.push_back( args );
}
return devices;
}
@ -115,17 +120,33 @@ size_t file_source_c::get_num_channels( void )
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 range;
range += osmosdr::range_t( get_sample_rate() );
range += osmosdr::range_t( _file_rate ); /* always return file's original rate */
return range;
}
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();
}

View File

@ -21,6 +21,8 @@
#define FILE_SOURCE_C_H
#include <gr_hier_block2.h>
#include <gr_file_source.h>
#include <gr_throttle.h>
#include "osmosdr_src_iface.h"
@ -44,10 +46,12 @@ public:
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 );
bool seek( long seek_point, int whence, size_t chan );
osmosdr::meta_range_t get_sample_rates( void );
double set_sample_rate( double rate );
double get_sample_rate( void );
@ -71,6 +75,10 @@ public:
std::string get_antenna( size_t chan = 0 );
private:
gr_file_source_sptr _source;
gr_throttle::sptr _throttle;
double _file_rate;
double _freq, _rate;
};

View File

@ -51,10 +51,18 @@
using namespace boost::assign;
#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 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)
{
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
* 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.
* only 1 input and 0 output.
*/
static const int MIN_IN = 1; // mininum 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),
_auto_gain(false),
_amp_gain(0),
_vga_gain(0)
_vga_gain(0),
_bandwidth(0)
{
int ret;
uint16_t val;
dict_t dict = params_to_dict(args);
@ -177,24 +185,20 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
_dev = NULL;
ret = hackrf_open( &_dev );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to open HackRF device.");
HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device")
uint8_t board_id;
ret = hackrf_board_id_read( _dev, &board_id );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to get board id.");
HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id")
char version[40];
memset(version, 0, sizeof(version));
ret = hackrf_version_string_read( _dev, version, sizeof(version));
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to read version string.");
HACKRF_THROW_ON_ERROR(ret, "Failed to read version string")
#if 0
read_partid_serialno_t serial_number;
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to read serial number.");
HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number")
#endif
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
<< "with firmware " << version << " "
@ -205,21 +209,22 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
<< 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 */
hackrf_max2837_read( _dev, 29, &val );
val |= 0x3; /* enable TX VGA control over SPI */
hackrf_max2837_write( _dev, 29, val );
set_gain( 14 ); /* enable AMP gain stage by default */
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 );
// _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) {
// _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;
{
@ -292,13 +300,13 @@ bool hackrf_sink_c::start()
return false;
_buf_used = 0;
#if 0
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;
return false;
}
#endif
return true;
}
@ -306,22 +314,20 @@ bool hackrf_sink_c::stop()
{
if ( ! _dev )
return false;
#if 0
int ret = hackrf_stop_tx( _dev );
if (ret != HACKRF_SUCCESS) {
if ( ret != HACKRF_SUCCESS ) {
std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl;
return false;
}
#endif
return true;
}
#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);
__m128i addme = _mm_set_epi16(127, 127, 127, 127, 127, 127, 127, 127);
for(unsigned int i=0; i<count;i++){
__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 a2 = _mm256_castsi256_si128(itmp4);
__m128i outshorts1 = _mm_add_epi16(_mm_packs_epi32(a0, a1), addme);
__m128i outshorts2 = _mm_add_epi16(_mm_packs_epi32(a2, a3), addme);
__m128i outbytes = _mm_packus_epi16(outshorts1, outshorts2);
__m128i outshorts1 = _mm_packs_epi32(a0, a1);
__m128i outshorts2 = _mm_packs_epi32(a2, a3);
__m128i outbytes = _mm_packs_epi16(outshorts1, outshorts2);
_mm_storeu_si128 ((__m128i*)&outbuf[i*16], outbytes);
}
}
#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 );
__m128i addme = _mm_set_epi16( 127, 127, 127, 127, 127, 127, 127, 127);
__m128 itmp1,itmp2,itmp3,itmp4;
__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);
otmp4 = _mm_cvtps_epi32(itmp4);
outshorts1 = _mm_add_epi16(_mm_packs_epi32(otmp1, otmp2), addme);
outshorts2 = _mm_add_epi16(_mm_packs_epi32(otmp3, otmp4), addme);
outshorts1 = _mm_packs_epi32(otmp1, otmp2);
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);
}
}
#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++){
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 );
}
unsigned char *buf = _buf + _buf_used;
char *buf = _buf + _buf_used;
unsigned int prev_buf_used = _buf_used;
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::string label;
#if 0
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; 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 + "'";
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;
}
@ -468,7 +516,11 @@ osmosdr::meta_range_t hackrf_sink_c::get_sample_rates()
{
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( 12.5e6 );
range += osmosdr::range_t( 16e6 );
@ -477,17 +529,17 @@ osmosdr::meta_range_t hackrf_sink_c::get_sample_rates()
return range;
}
double hackrf_sink_c::set_sample_rate(double rate)
double hackrf_sink_c::set_sample_rate( double rate )
{
int ret;
if (_dev) {
ret = hackrf_sample_rate_set( _dev, uint32_t(rate) );
ret = hackrf_set_sample_rate( _dev, rate );
if ( HACKRF_SUCCESS == ret ) {
_sample_rate = rate;
set_bandwidth( rate );
//set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */
} 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 ) {
_center_freq = freq;
} 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 )
{
int ret;
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
if (_dev) {
double clip_gain = rf_gains.clip( gain, true );
uint8_t value = clip_gain == 14.0f ? 1 : 0;
std::map<double, int> reg_vals;
reg_vals[ 0 ] = 0;
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;
ret = hackrf_set_amp_enable( _dev, value );
if ( HACKRF_SUCCESS == ret ) {
_amp_gain = clip_gain;
} else {
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_amp_enable", value ) )
}
}
@ -646,71 +691,24 @@ 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 )
{
int ret;
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) {
int value = 0;
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 );
double clip_gain = if_gains.clip( gain, true );
val = (val & 0xf) | ((value & 0x3f) << 4);
if ( hackrf_max2837_write( _dev, 29, val ) == HACKRF_SUCCESS )
ret = hackrf_set_txvga_gain( _dev, uint32_t(clip_gain) );
if ( HACKRF_SUCCESS == ret ) {
_vga_gain = clip_gain;
} else {
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_txvga_gain", clip_gain ) )
}
}
return _vga_gain;
}
double hackrf_sink_c::set_bb_gain(double gain, size_t chan)
double hackrf_sink_c::set_bb_gain( double gain, size_t chan )
{
return 0;
}
@ -740,16 +738,16 @@ double hackrf_sink_c::set_bandwidth( double bandwidth, size_t chan )
// osmosdr::freq_range_t bandwidths = get_bandwidth_range( chan );
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 ) {
/* compute best default value depending on sample rate (auto filter) */
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 ) {
_bandwidth = bw;
} else {
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) )
}
}

View File

@ -134,7 +134,7 @@ private:
// gruel::thread _thread;
circular_buffer_t _cbuf;
unsigned char *_buf;
char *_buf;
unsigned int _buf_num;
unsigned int _buf_used;
boost::mutex _buf_mutex;

View File

@ -27,8 +27,8 @@
#include "config.h"
#endif
#include "hackrf_source_c.h"
#include <gnuradio/gr_io_signature.h>
#include <stdexcept>
#include <iostream>
#include <boost/assign.hpp>
#include <boost/format.hpp>
@ -36,18 +36,27 @@
#include <boost/algorithm/string.hpp>
#include <boost/thread/thread.hpp>
#include <stdexcept>
#include <iostream>
#include <gnuradio/gr_io_signature.h>
#include "hackrf_source_c.h"
#include <osmosdr_arg_helpers.h>
using namespace boost::assign;
#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 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;
boost::mutex hackrf_source_c::_usage_mutex;
@ -85,10 +94,10 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
_auto_gain(false),
_amp_gain(0),
_lna_gain(0),
_vga_gain(0)
_vga_gain(0),
_bandwidth(0)
{
int ret;
uint16_t val;
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
for (unsigned int i = 0; i <= 0xffff; i++) {
#ifdef BOOST_LITTLE_ENDIAN
_lut.push_back( gr_complex( (float(i & 0xff) - 127.5f) * (1.0f/128.0f),
(float(i >> 8) - 127.5f) * (1.0f/128.0f) ) );
_lut.push_back( gr_complex( (float(char(i & 0xff))) * (1.0f/128.0f),
(float(char(i >> 8))) * (1.0f/128.0f) ) );
#else // BOOST_BIG_ENDIAN
_lut.push_back( gr_complex( (float(i >> 8) - 127.5f) * (1.0f/128.0f),
(float(i & 0xff) - 127.5f) * (1.0f/128.0f) ) );
_lut.push_back( gr_complex( (float(char(i >> 8))) * (1.0f/128.0f),
(float(char(i & 0xff))) * (1.0f/128.0f) ) );
#endif
}
@ -130,24 +139,21 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
_dev = NULL;
ret = hackrf_open( &_dev );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to open HackRF device.");
HACKRF_THROW_ON_ERROR(ret, "Failed to open HackRF device")
uint8_t board_id;
ret = hackrf_board_id_read( _dev, &board_id );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to get board id.");
HACKRF_THROW_ON_ERROR(ret, "Failed to get HackRF board id")
char version[40];
memset(version, 0, sizeof(version));
ret = hackrf_version_string_read( _dev, version, sizeof(version));
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to read version string.");
HACKRF_THROW_ON_ERROR(ret, "Failed to read version string")
#if 0
read_partid_serialno_t serial_number;
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
if (ret != HACKRF_SUCCESS)
throw std::runtime_error("Failed to read serial number.");
HACKRF_THROW_ON_ERROR(ret, "Failed to read serial number")
#endif
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
<< "with firmware " << version << " "
@ -158,13 +164,11 @@ hackrf_source_c::hackrf_source_c (const std::string &args)
<< 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 */
hackrf_max2837_read( _dev, 8, &val );
val |= 0x3; /* enable LNA & VGA control over SPI */
hackrf_max2837_write( _dev, 8, val );
set_gain( 14 ); /* enable AMP gain stage by default */
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);
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) {
// _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;
{
@ -251,13 +261,13 @@ bool hackrf_source_c::start()
{
if ( ! _dev )
return false;
#if 0
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;
return false;
}
#endif
return true;
}
@ -265,13 +275,13 @@ bool hackrf_source_c::stop()
{
if ( ! _dev )
return false;
#if 0
int ret = hackrf_stop_rx( _dev );
if (ret != HACKRF_SUCCESS) {
if ( ret != HACKRF_SUCCESS ) {
std::cerr << "Failed to stop RX streaming (" << ret << ")" << std::endl;
return false;
}
#endif
return true;
}
@ -333,7 +343,7 @@ std::vector<std::string> hackrf_source_c::get_devices()
{
std::vector<std::string> devices;
std::string label;
#if 0
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; 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 + "'";
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;
}
@ -359,7 +411,11 @@ osmosdr::meta_range_t hackrf_source_c::get_sample_rates()
{
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( 12.5e6 );
range += osmosdr::range_t( 16e6 );
@ -368,17 +424,17 @@ osmosdr::meta_range_t hackrf_source_c::get_sample_rates()
return range;
}
double hackrf_source_c::set_sample_rate(double rate)
double hackrf_source_c::set_sample_rate( double rate )
{
int ret;
if (_dev) {
ret = hackrf_sample_rate_set( _dev, uint32_t(rate) );
ret = hackrf_set_sample_rate( _dev, rate );
if ( HACKRF_SUCCESS == ret ) {
_sample_rate = rate;
set_bandwidth( rate );
//set_bandwidth( 0.0 ); /* bandwidth of 0 means automatic filter selection */
} 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 ) {
_center_freq = freq;
} 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 )
{
int ret;
osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan );
if (_dev) {
double clip_gain = rf_gains.clip( gain, true );
uint8_t value = clip_gain == 14.0f ? 1 : 0;
std::map<double, int> reg_vals;
reg_vals[ 0 ] = 0;
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;
ret = hackrf_set_amp_enable( _dev, value );
if ( HACKRF_SUCCESS == ret ) {
_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)
{
int ret;
osmosdr::gain_range_t rf_gains = get_gain_range( "IF", chan );
if (_dev) {
double clip_gain = rf_gains.clip( gain, true );
double rel_gain = fabs( rf_gains.stop() - clip_gain );
std::map<double, int> reg_vals;
reg_vals[ 0 ] = 0;
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;
ret = hackrf_set_lna_gain( _dev, uint32_t(clip_gain) );
if ( HACKRF_SUCCESS == ret ) {
_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 )
{
int ret;
osmosdr::gain_range_t if_gains = get_gain_range( "BB", chan );
if (_dev) {
double clip_gain = if_gains.clip( gain, true );
double rel_gain = fabs( if_gains.stop() - clip_gain );
std::map<double, int> reg_vals;
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;
ret = hackrf_set_vga_gain( _dev, uint32_t(clip_gain) );
if ( HACKRF_SUCCESS == ret ) {
_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 );
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 ) {
/* compute best default value depending on sample rate (auto filter) */
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 ) {
_bandwidth = bw;
} else {
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
HACKRF_THROW_ON_ERROR( ret, HACKRF_FUNC_STR( "hackrf_set_baseband_filter_bandwidth", bw ) )
}
}

View File

@ -23,7 +23,7 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${LIBMIRISDR_INCLUDE_DIR}
${LIBMIRISDR_INCLUDE_DIRS}
)
set(mirisdr_srcs

View File

@ -45,7 +45,7 @@
using namespace boost::assign;
#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 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()
{
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;

View File

@ -23,7 +23,7 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${LIBOSMOSDR_INCLUDE_DIR}
${LIBOSMOSDR_INCLUDE_DIRS}
)
set(osmosdr_srcs

View File

@ -44,7 +44,7 @@
using namespace boost::assign;
#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 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()
{
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;

View File

@ -102,7 +102,7 @@ struct is_nchan_argument
{
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 )
{
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 );
max_nchan = boost::lexical_cast<size_t>( pair.second );

View File

@ -62,6 +62,18 @@
#include <hackrf_source_c.h>
#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"
using namespace osmosdr;
@ -111,6 +123,11 @@ devices_t device::find(const device_t &hint)
{
boost::mutex::scoped_lock lock(_device_mutex);
bool fake = true;
if ( hint.count("nofake") )
fake = false;
devices_t devices;
#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() )
devices.push_back( device_t(dev) );
#endif
#ifdef ENABLE_BLADERF
BOOST_FOREACH( std::string dev, bladerf_source_c::get_devices() )
devices.push_back( device_t(dev) );
#endif
#ifdef ENABLE_HACKRF
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
devices.push_back( device_t(dev) );
devices.push_back( device_t(dev) );
#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,
* hopefully resulting in hardware sources to be shown first
* in a graphical interface etc... */
#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) );
#endif
#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) );
#endif

View File

@ -40,6 +40,9 @@
#ifdef ENABLE_HACKRF
#include "hackrf_sink_c.h"
#endif
#ifdef ENABLE_BLADERF
#include "bladerf_sink_c.h"
#endif
#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)
: gr_hier_block2 ("osmosdr_sink_c_impl",
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;
bool device_specified = false;
@ -78,10 +82,13 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
#ifdef ENABLE_HACKRF
dev_types.push_back("hackrf");
#endif
#ifdef ENABLE_BLADERF
dev_types.push_back("bladerf");
#endif
std::cerr << "gr-osmosdr "
<< GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr_version() << std::endl;
std::cerr << "built-in sink types: ";
BOOST_FOREACH(std::string dev_type, dev_types)
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() )
dev_list.push_back( dev );
#endif
#ifdef ENABLE_BLADERF
BOOST_FOREACH( std::string dev, bladerf_sink_c::get_devices() )
dev_list.push_back( dev );
#endif
#ifdef ENABLE_HACKRF
BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() )
dev_list.push_back( dev );
#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// 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();
}
#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 ) {
_devs.push_back( iface );
@ -160,12 +178,14 @@ osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
} catch ( std::exception &ex ) {
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
<< " missing channel(s) with null sinks.\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;
for (size_t i = 0; i < missing_chans; i++) {
@ -252,11 +272,12 @@ double osmosdr_sink_c_impl::set_center_freq( double freq, 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++ )
if ( chan == channel++ ) {
if ( _center_freq[ chan ] != freq ) {
_center_freq[ chan ] = freq;
return dev->set_center_freq( freq, dev_chan );
}
} else { return _center_freq[ chan ]; }
}
return 0;
}
@ -277,11 +298,12 @@ double osmosdr_sink_c_impl::set_freq_corr( double ppm, 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++ )
if ( chan == channel++ ) {
if ( _freq_corr[ chan ] != ppm ) {
_freq_corr[ chan ] = ppm;
return dev->set_freq_corr( ppm, dev_chan );
}
} else { return _freq_corr[ chan ]; }
}
return 0;
}
@ -335,14 +357,15 @@ bool osmosdr_sink_c_impl::set_gain_mode( bool automatic, 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++ )
if ( chan == channel++ ) {
if ( _gain_mode[ chan ] != automatic ) {
_gain_mode[ chan ] = automatic;
bool mode = dev->set_gain_mode( automatic, dev_chan );
if (!automatic) // reapply gain value when switched to manual mode
dev->set_gain( _gain[ chan ], dev_chan );
return mode;
}
} else { return _gain_mode[ chan ]; }
}
return false;
}
@ -363,11 +386,12 @@ double osmosdr_sink_c_impl::set_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _gain[ chan ] != gain ) {
_gain[ chan ] = gain;
return dev->set_gain( gain, dev_chan );
}
} else { return _gain[ chan ]; }
}
return 0;
}
@ -410,11 +434,12 @@ double osmosdr_sink_c_impl::set_if_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _if_gain[ chan ] != gain ) {
_if_gain[ chan ] = gain;
return dev->set_if_gain( gain, dev_chan );
}
} else { return _if_gain[ chan ]; }
}
return 0;
}
@ -424,11 +449,12 @@ double osmosdr_sink_c_impl::set_bb_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _bb_gain[ chan ] != gain ) {
_bb_gain[ chan ] = gain;
return dev->set_bb_gain( gain, dev_chan );
}
} else { return _bb_gain[ chan ]; }
}
return 0;
}
@ -449,11 +475,12 @@ std::string osmosdr_sink_c_impl::set_antenna( const std::string & antenna, size_
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++ )
if ( chan == channel++ ) {
if ( _antenna[ chan ] != antenna ) {
_antenna[ chan ] = antenna;
return dev->set_antenna( antenna, dev_chan );
}
} else { return _antenna[ chan ]; }
}
return "";
}
@ -469,14 +496,22 @@ std::string osmosdr_sink_c_impl::get_antenna( size_t chan )
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 )
@ -484,11 +519,12 @@ double osmosdr_sink_c_impl::set_bandwidth( double bandwidth, 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++ )
if ( _bandwidth[ chan ] != bandwidth ) {
if ( chan == channel++ ) {
if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
_bandwidth[ chan ] = bandwidth;
return dev->set_bandwidth( bandwidth, dev_chan );
}
} else { return _bandwidth[ chan ]; }
}
return 0;
}

View File

@ -58,8 +58,9 @@ public:
std::string set_antenna( const std::string & 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_iq_balance( const std::complex<double> &correction, 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 );
@ -74,6 +75,7 @@ private:
std::vector< osmosdr_snk_iface * > _devs;
/* cache to prevent multiple device calls with the same value coming from grc */
double _sample_rate;
std::map< size_t, double > _center_freq;
std::map< size_t, double > _freq_corr;

View File

@ -218,25 +218,26 @@ public:
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
*/
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.
* 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
*/
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.
* \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
* \return the actual filter bandwidth in Hz
*/

View File

@ -65,6 +65,18 @@
#include <hackrf_source_c.h>
#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>
/* 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)
: gr_hier_block2 ("osmosdr_source_c_impl",
gr_make_io_signature (0, 0, 0),
args_to_io_signature(args))
args_to_io_signature(args)),
_sample_rate(NAN)
{
size_t channel = 0;
bool device_specified = false;
@ -120,15 +133,30 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
#ifdef ENABLE_HACKRF
dev_types.push_back("hackrf");
#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 "
<< GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") "
<< GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") "
<< "gnuradio " << gr_version() << std::endl;
std::cerr << "built-in source types: ";
BOOST_FOREACH(std::string dev_type, dev_types)
std::cerr << dev_type << " ";
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) {
dict_t dict = params_to_dict(arg);
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() )
dev_list.push_back( dev );
#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
BOOST_FOREACH( std::string dev, hackrf_source_c::get_devices() )
dev_list.push_back( dev );
#endif
#ifdef ENABLE_AIRSPY
BOOST_FOREACH( std::string dev, airspy_source_c::get_devices() )
dev_list.push_back( dev );
#endif
// std::cerr << std::endl;
// BOOST_FOREACH( std::string dev, dev_list )
// std::cerr << "'" << dev << "'" << std::endl;
@ -244,6 +285,30 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
}
#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 ) {
_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);
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
<< " missing channel(s) with gaussian noise.\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;
for (size_t i = 0; i < missing_chans; i++)
@ -308,6 +375,17 @@ size_t osmosdr_source_c_impl::get_num_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."
osmosdr::meta_range_t osmosdr_source_c_impl::get_sample_rates()
@ -386,11 +464,12 @@ double osmosdr_source_c_impl::set_center_freq( double freq, 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++ )
if ( chan == channel++ ) {
if ( _center_freq[ chan ] != freq ) {
_center_freq[ chan ] = freq;
return dev->set_center_freq( freq, dev_chan );
}
} else { return _center_freq[ chan ]; }
}
return 0;
}
@ -411,11 +490,12 @@ double osmosdr_source_c_impl::set_freq_corr( double ppm, 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++ )
if ( chan == channel++ ) {
if ( _freq_corr[ chan ] != ppm ) {
_freq_corr[ chan ] = ppm;
return dev->set_freq_corr( ppm, dev_chan );
}
} else { return _freq_corr[ chan ]; }
}
return 0;
}
@ -469,14 +549,15 @@ bool osmosdr_source_c_impl::set_gain_mode( bool automatic, 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++ )
if ( chan == channel++ ) {
if ( _gain_mode[ chan ] != automatic ) {
_gain_mode[ chan ] = automatic;
bool mode = dev->set_gain_mode( automatic, dev_chan );
if (!automatic) // reapply gain value when switched to manual mode
dev->set_gain( _gain[ chan ], dev_chan );
return mode;
}
} else { return _gain_mode[ chan ]; }
}
return false;
}
@ -497,11 +578,12 @@ double osmosdr_source_c_impl::set_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _gain[ chan ] != gain ) {
_gain[ chan ] = gain;
return dev->set_gain( gain, dev_chan );
}
} else { return _gain[ chan ]; }
}
return 0;
}
@ -544,11 +626,12 @@ double osmosdr_source_c_impl::set_if_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _if_gain[ chan ] != gain ) {
_if_gain[ chan ] = gain;
return dev->set_if_gain( gain, dev_chan );
}
} else { return _if_gain[ chan ]; }
}
return 0;
}
@ -558,11 +641,12 @@ double osmosdr_source_c_impl::set_bb_gain( double gain, 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++ )
if ( chan == channel++ ) {
if ( _bb_gain[ chan ] != gain ) {
_bb_gain[ chan ] = gain;
return dev->set_bb_gain( gain, dev_chan );
}
} else { return _bb_gain[ chan ]; }
}
return 0;
}
@ -583,11 +667,12 @@ std::string osmosdr_source_c_impl::set_antenna( const std::string & antenna, siz
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++ )
if ( chan == channel++ ) {
if ( _antenna[ chan ] != antenna ) {
_antenna[ chan ] = antenna;
return dev->set_antenna( antenna, dev_chan );
}
} else { return _antenna[ chan ]; }
}
return "";
}
@ -603,10 +688,28 @@ std::string osmosdr_source_c_impl::get_antenna( size_t chan )
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 )
{
#ifdef HAVE_IQBALANCE
size_t channel = 0;
#ifdef HAVE_IQBALANCE
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
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
}
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;
#ifdef HAVE_IQBALANCE
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) {
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) {
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];
if ( opt->period() == 0 ) { /* automatic optimization desabled */
fix->set_mag( correction.real() );
fix->set_phase( correction.imag() );
fix->set_mag( balance.real() );
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
}
@ -666,11 +779,12 @@ double osmosdr_source_c_impl::set_bandwidth( double bandwidth, 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++ )
if ( _bandwidth[ chan ] != bandwidth ) {
if ( chan == channel++ ) {
if ( _bandwidth[ chan ] != bandwidth || 0.0f == bandwidth ) {
_bandwidth[ chan ] = bandwidth;
return dev->set_bandwidth( bandwidth, dev_chan );
}
} else { return _bandwidth[ chan ]; }
}
return 0;
}

View File

@ -36,6 +36,8 @@ class osmosdr_source_c_impl : public osmosdr_source_c
public:
size_t get_num_channels( void );
bool seek( long seek_point, int whence, size_t chan );
osmosdr::meta_range_t get_sample_rates( void );
double set_sample_rate( double rate );
double get_sample_rate( void );
@ -63,8 +65,11 @@ public:
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> &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 get_bandwidth( size_t chan = 0 );
@ -79,6 +84,7 @@ private:
std::vector< osmosdr_src_iface * > _devs;
/* cache to prevent multiple device calls with the same value coming from grc */
double _sample_rate;
std::map< size_t, double > _center_freq;
std::map< size_t, double > _freq_corr;

View File

@ -41,6 +41,15 @@ public:
*/
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.
* \return a range of rates in Sps
@ -217,6 +226,30 @@ public:
*/
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.
*
@ -229,14 +262,14 @@ public:
* Set the RX frontend IQ balance correction.
* 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
*/
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.
* \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
* \return the actual filter bandwidth in Hz
*/

View 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}
)
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

View File

@ -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 */

View File

@ -23,7 +23,7 @@
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}
${LIBRTLSDR_INCLUDE_DIR}
${LIBRTLSDR_INCLUDE_DIRS}
)
set(rtl_srcs

View File

@ -46,7 +46,7 @@
using namespace boost::assign;
#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 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))),
_dev(NULL),
_buf(NULL),
_running(true),
_running(false),
_no_tuner(false),
_auto_gain(false),
_if_gain(0),
@ -101,14 +101,18 @@ rtl_source_c::rtl_source_c (const std::string &args)
dict_t dict = params_to_dict(args);
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 */
} else { /* use the numeric value of the argument */
try {
dev_index = boost::lexical_cast< unsigned int >( dict["rtl"] );
} catch ( std::exception &ex ) {
throw std::runtime_error(
"Failed to use '" + dict["rtl"] + "' as index: " + ex.what());
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());
}
}
}
}
@ -170,11 +174,11 @@ rtl_source_c::rtl_source_c (const std::string &args)
// create a lookup table for gr_complex values
for (unsigned int i = 0; i <= 0xffff; i++) {
#ifdef BOOST_LITTLE_ENDIAN
_lut.push_back( gr_complex( (float(i & 0xff) - 127.5f) * (1.0f/128.0f),
(float(i >> 8) - 127.5f) * (1.0f/128.0f) ) );
_lut.push_back( gr_complex( (float(i & 0xff) - 127.4f) * (1.0f/128.0f),
(float(i >> 8) - 127.4f) * (1.0f/128.0f) ) );
#else // BOOST_BIG_ENDIAN
_lut.push_back( gr_complex( (float(i >> 8) - 127.5f) * (1.0f/128.0f),
(float(i & 0xff) - 127.5f) * (1.0f/128.0f) ) );
_lut.push_back( gr_complex( (float(i >> 8) - 127.4f) * (1.0f/128.0f),
(float(i & 0xff) - 127.4f) * (1.0f/128.0f) ) );
#endif
}
@ -232,8 +236,6 @@ rtl_source_c::rtl_source_c (const std::string &args)
for(unsigned int i = 0; i < _buf_num; ++i)
_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 ()
{
if (_dev) {
_running = false;
rtlsdr_cancel_async( _dev );
_thread.join();
if (_running)
{
_running = false;
rtlsdr_cancel_async( _dev );
_thread.join();
}
rtlsdr_close( _dev );
_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)
{
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()
{
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;
@ -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( 2048000 ); // 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( 2800000 ); // 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 );
} else if ( tuner == RTLSDR_TUNER_R820T ) {
range += osmosdr::range_t( 24e6, 1766e6 );
} else if ( tuner == RTLSDR_TUNER_R828D ) {
range += osmosdr::range_t( 24e6, 1766e6 );
}
}

View File

@ -112,6 +112,10 @@ public:
std::string set_antenna( const std::string & antenna, size_t chan = 0 );
std::string get_antenna( size_t chan = 0 );
protected:
bool start();
bool stop();
private:
static void _rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx);
void rtlsdr_callback(unsigned char *buf, uint32_t len);

View File

@ -47,6 +47,8 @@ static std::string get_tuner_name( enum rtlsdr_tuner tuner_type )
return "FC2580";
else if ( RTLSDR_TUNER_R820T == tuner_type )
return "R820T";
else if ( RTLSDR_TUNER_R828D == tuner_type )
return "R828D";
else
return "Unknown";
}
@ -147,13 +149,16 @@ std::string rtl_tcp_source_c::name()
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::string args = "rtl_tcp=localhost:1234";
args += ",label='RTL-SDR Spectrum Server'";
devices.push_back( args );
if ( fake )
{
std::string args = "rtl_tcp=localhost:1234";
args += ",label='RTL-SDR Spectrum Server'";
devices.push_back( args );
}
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( 2048000 ); // 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( 2800000 ); // 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 );
} else if ( tuner == RTLSDR_TUNER_R820T ) {
range += osmosdr::range_t( 24e6, 1766e6 );
} else if ( tuner == RTLSDR_TUNER_R828D ) {
range += osmosdr::range_t( 24e6, 1766e6 );
} else {
range += osmosdr::range_t( 52e6, 2.2e9 ); // assume E4000 tuner
}

View File

@ -46,7 +46,7 @@ public:
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 );

View File

@ -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_LUT= new float[0xff+1];
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
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)
{
float *out = (float *) output_items[0];
ssize_t r=0, nbytes=0, bytes_received=0;
ssize_t total_bytes = (ssize_t)(d_itemsize*noutput_items);
ssize_t r = 0;
int bytesleft = noutput_items;
int index = 0;

View File

@ -51,7 +51,8 @@ enum rtlsdr_tuner {
RTLSDR_TUNER_FC0012,
RTLSDR_TUNER_FC0013,
RTLSDR_TUNER_FC2580,
RTLSDR_TUNER_R820T
RTLSDR_TUNER_R820T,
RTLSDR_TUNER_R828D
};
class rtl_tcp_source_f;

View File

@ -30,6 +30,9 @@
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)
{
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",
args_to_io_signature(args),
gr_make_io_signature (0, 0, 0)),
_center_freq(0.0f),
_freq_corr(0.0f),
_lo_offset(0.0f)
{
size_t nchan = 1;
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"))
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 &&
"nchan" != 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 + ",";
}
}
@ -66,16 +82,114 @@ uhd_sink_c::uhd_sink_c(const std::string &args) :
_snk = uhd_make_usrp_sink( arguments,
uhd::io_type_t::COMPLEX_FLOAT32,
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")) {
_snk->set_subdev_spec( dict["subdev"] );
std::cerr << "-- Using subdev spec '" << _snk->get_subdev_spec() << "'."
<< std::endl;
}
std::cerr << "-- Using subdev spec '" << _snk->get_subdev_spec() << "'."
<< std::endl;
if (0.0 != _lo_offset)
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++ )
connect( self(), i, _snk, i );
@ -90,14 +204,18 @@ std::vector< std::string > uhd_sink_c::get_devices()
std::vector< std::string > devices;
uhd::device_addr_t hint;
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint)) {
std::string args = "uhd,subdev='A:0'," + dev.to_string();
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint))
{
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 type = dev.cast< std::string >("type", "USRP");
std::string name = dev.cast< std::string >("name", "");
std::string serial = dev.cast< std::string >("serial", "");
if ( "umtrx" == type )
label = "Fairwaves";
if (type.length()) {
boost::to_upper(type);
@ -105,7 +223,7 @@ std::vector< std::string > uhd_sink_c::get_devices()
}
if (name.length())
label += " " + name;
label += " (" + name + ")";
if (serial.length())
label += " " + serial;
@ -115,9 +233,6 @@ std::vector< std::string > uhd_sink_c::get_devices()
devices.push_back( args );
}
//devices.clear();
//devices.push_back( "uhd,type=usrp1,label='Ettus USRP'" );
return devices;
}
@ -136,7 +251,8 @@ std::string uhd_sink_c::name()
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 )
@ -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 )
{
//_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
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);
_center_freq = freq;
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 )
{
_freq_corr = ppm;
set_center_freq( _center_freq );
return get_freq_corr(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 )
@ -262,3 +385,65 @@ std::string uhd_sink_c::get_antenna( size_t 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;
}

View File

@ -71,7 +71,17 @@ public:
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 = 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:
double _center_freq;
double _freq_corr;
double _lo_offset;
boost::shared_ptr<uhd_usrp_sink> _snk;
};

View File

@ -21,12 +21,16 @@
#include <boost/foreach.hpp>
#include <boost/assign.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>
//#include <uhd/property_tree.hpp>
#include "osmosdr_arg_helpers.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;
@ -36,13 +40,20 @@ uhd_source_c_sptr make_uhd_source_c(const std::string &args)
}
uhd_source_c::uhd_source_c(const std::string &args) :
gr_hier_block2("uhd_source_c",
gr_make_io_signature (0, 0, 0),
args_to_io_signature(args)),
_lo_offset(0.0f)
gr_hier_block2("uhd_source_c",
gr_make_io_signature (0, 0, 0),
args_to_io_signature(args)),
_center_freq(0.0f),
_freq_corr(0.0f),
_lo_offset(0.0f)
{
size_t nchan = 1;
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"))
nchan = boost::lexical_cast< size_t >( dict["nchan"] );
@ -58,24 +69,125 @@ uhd_source_c::uhd_source_c(const std::string &args) :
if ( "uhd" != entry.first &&
"nchan" != 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 + ",";
}
}
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,
uhd::io_type_t::COMPLEX_FLOAT32,
nchan );
stream_args );
if (dict.count("subdev")) {
_src->set_subdev_spec( dict["subdev"] );
std::cerr << "-- Using subdev spec '" << _src->get_subdev_spec() << "'."
<< std::endl;
}
std::cerr << "-- Using subdev spec '" << _src->get_subdev_spec() << "'."
<< std::endl;
if (0.0 != _lo_offset)
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++ )
connect( _src, i, self(), i );
@ -90,14 +202,18 @@ std::vector< std::string > uhd_source_c::get_devices()
std::vector< std::string > devices;
uhd::device_addr_t hint;
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint)) {
std::string args = "uhd,subdev='A:0'," + dev.to_string();
BOOST_FOREACH(const uhd::device_addr_t &dev, uhd::device::find(hint))
{
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 type = dev.cast< std::string >("type", "USRP");
std::string name = dev.cast< std::string >("name", "");
std::string serial = dev.cast< std::string >("serial", "");
if ( "umtrx" == type )
label = "Fairwaves";
if (type.length()) {
boost::to_upper(type);
@ -105,7 +221,7 @@ std::vector< std::string > uhd_source_c::get_devices()
}
if (name.length())
label += " " + name;
label += " (" + name + ")";
if (serial.length())
label += " " + serial;
@ -115,9 +231,6 @@ std::vector< std::string > uhd_source_c::get_devices()
devices.push_back( args );
}
//devices.clear();
//devices.push_back( "uhd,type=usrp1,label='Ettus USRP'" );
return devices;
}
@ -136,7 +249,8 @@ std::string uhd_source_c::name()
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 )
@ -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 )
{
//_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
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);
_center_freq = freq;
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 )
{
return get_freq_corr(chan);
_freq_corr = ppm;
set_center_freq( _center_freq );
return get_freq_corr( 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 )
@ -262,3 +383,95 @@ std::string uhd_source_c::get_antenna( size_t 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;
}

View File

@ -71,7 +71,19 @@ public:
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:
double _center_freq;
double _freq_corr;
double _lo_offset;
boost::shared_ptr<uhd_usrp_source> _src;
};

View File

@ -21,7 +21,7 @@
# Include swig generation macros
########################################################################
find_package(SWIG)
find_package(PythonLibs)
find_package(PythonLibs 2)
if(NOT SWIG_FOUND OR NOT PYTHONLIBS_FOUND)
return()
endif()