├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── Changelog.txt ├── FindLIBHACKRF.cmake ├── HackRF_Registration.cpp ├── HackRF_Session.cpp ├── HackRF_Settings.cpp ├── HackRF_Streaming.cpp ├── LICENSE ├── README.md ├── SoapyHackRF.hpp ├── cmake └── cmake_uninstall.cmake.in ├── debian ├── changelog ├── compat ├── control ├── copyright ├── docs ├── rules ├── soapysdr0.7-module-hackrf.install └── source │ └── format └── self_test.py /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | ## Travis CI config for SoapyHackRF 3 | ## 4 | ## * installs SoapySDR from source 5 | ## * confirms build and install 6 | ## * checks that drivers load 7 | ######################################################################## 8 | 9 | sudo: required 10 | dist: focal 11 | 12 | language: cpp 13 | compiler: gcc 14 | 15 | env: 16 | global: 17 | - INSTALL_PREFIX=/usr/local 18 | - SOAPY_SDR_BRANCH=master 19 | matrix: 20 | - BUILD_TYPE=Debug 21 | - BUILD_TYPE=Release 22 | 23 | before_install: 24 | # regular ubuntu packages 25 | - sudo add-apt-repository main 26 | - sudo add-apt-repository universe 27 | 28 | # update after package changes 29 | - sudo apt-get update 30 | 31 | install: 32 | #sdr development files 33 | - sudo apt-get install --no-install-recommends -q -y libhackrf-dev 34 | 35 | # install SoapySDR from source 36 | - git clone https://github.com/pothosware/SoapySDR.git 37 | - pushd SoapySDR 38 | - git checkout ${SOAPY_SDR_BRANCH} 39 | - mkdir build && cd build 40 | - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DENABLE_PYTHON=OFF -DENABLE_PYTHON3=OFF 41 | - make && sudo make install 42 | - popd 43 | 44 | script: 45 | - mkdir build && cd build 46 | - cmake ../ -DCMAKE_INSTALL_PREFIX=${INSTALL_PREFIX} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} 47 | - make && sudo make install 48 | # print info about the install 49 | - export LD_LIBRARY_PATH=${INSTALL_PREFIX}/lib:${LD_LIBRARY_PATH} 50 | - export PATH=${INSTALL_PREFIX}/bin:${PATH} 51 | - SoapySDRUtil --info 52 | - SoapySDRUtil --check=hackrf 53 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ######################################################################## 2 | # Build Soapy SDR support module for HackRF 3 | ######################################################################## 4 | 5 | cmake_minimum_required(VERSION 2.8.7) 6 | project(SoapyHackRF CXX) 7 | 8 | find_package(SoapySDR "0.4.0" NO_MODULE) 9 | if (NOT SoapySDR_FOUND) 10 | message(FATAL_ERROR "Soapy SDR development files not found...") 11 | endif () 12 | 13 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 14 | find_package(LIBHACKRF) 15 | 16 | if (NOT LIBHACKRF_FOUND) 17 | message(FATAL_ERROR "HackRF development files not found...") 18 | endif () 19 | message(STATUS "LIBHACKRF_INCLUDE_DIRS - ${LIBHACKRF_INCLUDE_DIRS}") 20 | message(STATUS "LIBHACKRF_LIBRARIES - ${LIBHACKRF_LIBRARIES}") 21 | 22 | #version check for recent hackrf with device list API 23 | message(STATUS "Checking for hackrf_device_list API...") 24 | message(STATUS " Reading ${LIBHACKRF_INCLUDE_DIRS}/hackrf.h...") 25 | file(READ ${LIBHACKRF_INCLUDE_DIRS}/hackrf.h hackrf_h) 26 | string(FIND "${hackrf_h}" "hackrf_device_list" has_hackrf_device_list) 27 | if ("${has_hackrf_device_list}" STREQUAL "-1") 28 | message(FATAL_ERROR " libhackrf too old, missing hackrf_device_list API") 29 | endif() 30 | 31 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 32 | include_directories(${LIBHACKRF_INCLUDE_DIRS}) 33 | 34 | #enable c++11 features 35 | if(CMAKE_COMPILER_IS_GNUCXX) 36 | 37 | #C++11 is a required language feature for this project 38 | include(CheckCXXCompilerFlag) 39 | CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11) 40 | if(HAS_STD_CXX11) 41 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 42 | else(HAS_STD_CXX11) 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 44 | endif() 45 | 46 | #disable warnings for unused parameters 47 | add_definitions(-Wno-unused-parameter) 48 | 49 | endif(CMAKE_COMPILER_IS_GNUCXX) 50 | 51 | if (APPLE) 52 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wc++11-extensions") 53 | endif(APPLE) 54 | 55 | SOAPY_SDR_MODULE_UTIL( 56 | TARGET HackRFSupport 57 | SOURCES 58 | HackRF_Registration.cpp 59 | HackRF_Settings.cpp 60 | HackRF_Streaming.cpp 61 | HackRF_Session.cpp 62 | LIBRARIES ${LIBHACKRF_LIBRARIES} 63 | ) 64 | 65 | ######################################################################## 66 | # uninstall target 67 | ######################################################################## 68 | add_custom_target(uninstall 69 | "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") 70 | configure_file( 71 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" 72 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 73 | IMMEDIATE @ONLY) 74 | 75 | -------------------------------------------------------------------------------- /Changelog.txt: -------------------------------------------------------------------------------- 1 | Release 0.3.4 (2022-01-16) 2 | ========================== 3 | 4 | - Fix edge case return value of writeStream() 5 | - Fix edge case return value of readStream() 6 | - Added missing gain steps for getGainRange() 7 | 8 | Release 0.3.3 (2018-05-09) 9 | ========================== 10 | 11 | - Added check for hackrf_device_list API in header 12 | - Fixed getGain() for the Rx AMP due to minus typo 13 | 14 | Release 0.3.2 (2017-11-19) 15 | ========================== 16 | 17 | - Corrected order of gain elements in the Rx direction 18 | - Track settings during TX/RX switch for TRX switching 19 | - Prevent spin loop in SoapyHackRF::acquireReadBuffer() 20 | - Fix edge case in SoapyHackRF::releaseReadBuffer() 21 | - Standard style find script for FindLIBHACKRF.cmake 22 | 23 | Release 0.3.1 (2017-06-19) 24 | ========================== 25 | 26 | - Cache discovered HackRF results for claimed devices 27 | 28 | Release 0.3.0 (2017-04-29) 29 | ========================== 30 | 31 | - Major cleanup for thread safety and buffer management 32 | - Added label convention to hackrf discovery routine 33 | - Support filtering specific devices by serial number 34 | - Switch to format constants in streaming implementation 35 | 36 | Release 0.2.2 (2016-10-19) 37 | ========================== 38 | 39 | - New transceiver_mode_t enum for HackRF API changes 40 | - Clarified copyright statements in source files 41 | - Update debian files for SoapySDR module ABI format 42 | 43 | Release 0.2.1 (2016-02-29) 44 | ========================== 45 | 46 | - Fixed debian control file Maintainer/Uploaders 47 | - Reset buffer counters before activating RX stream 48 | - Rx stream switching waits for tx to be consumed 49 | 50 | Release 0.2.0 (2015-11-20) 51 | ========================== 52 | 53 | - Implemented Tx/Rx automatic stream switching 54 | - Implemented automatic gain distribution algorithm 55 | - Implemented the direct buffer access API 56 | - Implemented getStreamFormats() for SoapySDR v0.4 57 | - Implemented getNativeStreamFormat() for SoapySDR v0.4 58 | - Implemented getStreamArgsInfo() for SoapySDR v0.4 59 | - Created settings API calls for Bias TX control 60 | - Moved buffers device arg into stream args 61 | 62 | Release 0.1.0 (2015-10-10) 63 | ========================== 64 | 65 | - First release of SoapyHackRF support module 66 | -------------------------------------------------------------------------------- /FindLIBHACKRF.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find libhackrf 2 | # Once done this will define 3 | # LIBHACKRF_FOUND - System has libhackrf 4 | # LIBHACKRF_INCLUDE_DIRS - The libhackrf include directories 5 | # LIBHACKRF_LIBRARIES - The libraries needed to use libhackrf 6 | 7 | find_package(PkgConfig) 8 | pkg_check_modules(PC_LIBHACKRF QUIET libhackrf) 9 | 10 | find_path(LIBHACKRF_INCLUDE_DIR 11 | NAMES hackrf.h 12 | HINTS 13 | $ENV{LIBHACKRF_DIR}/include 14 | ${PC_LIBHACKRF_INCLUDEDIR} 15 | ${PC_LIBHACKRF_INCLUDE_DIRS} 16 | PATH_SUFFIXES libhackrf 17 | ) 18 | 19 | find_library(LIBHACKRF_LIBRARY 20 | NAMES hackrf 21 | HINTS 22 | $ENV{LIBHACKRF_DIR}/lib 23 | ${PC_LIBHACKRF_LIBDIR} 24 | ${PC_LIBHACKRF_LIBRARY_DIRS} 25 | ) 26 | 27 | include(FindPackageHandleStandardArgs) 28 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBHACKRF DEFAULT_MSG LIBHACKRF_LIBRARY LIBHACKRF_INCLUDE_DIR) 29 | 30 | mark_as_advanced(LIBHACKRF_INCLUDE_DIR LIBHACKRF_LIBRARY) 31 | 32 | set(LIBHACKRF_INCLUDE_DIRS ${LIBHACKRF_INCLUDE_DIR}) 33 | set(LIBHACKRF_LIBRARIES ${LIBHACKRF_LIBRARY}) 34 | -------------------------------------------------------------------------------- /HackRF_Registration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Wei Jiang 5 | * Copyright (c) 2015-2017 Josh Blum 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * The above copyright notice and this permission notice shall be included in 13 | * all copies or substantial portions of the Software. 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "SoapyHackRF.hpp" 24 | #include 25 | 26 | static std::map _cachedResults; 27 | 28 | static std::vector find_HackRF(const SoapySDR::Kwargs &args) 29 | { 30 | SoapyHackRFSession Sess; 31 | 32 | std::vector results; 33 | 34 | hackrf_device_list_t *list; 35 | 36 | list =hackrf_device_list(); 37 | 38 | if (list->devicecount > 0) { 39 | 40 | for (int i = 0; i < list->devicecount; i++) { 41 | 42 | hackrf_device* device = NULL; 43 | uint8_t board_id = BOARD_ID_INVALID; 44 | read_partid_serialno_t read_partid_serialno; 45 | 46 | hackrf_device_list_open(list, i, &device); 47 | 48 | SoapySDR::Kwargs options; 49 | 50 | if (device!=NULL) { 51 | 52 | hackrf_board_id_read(device, &board_id); 53 | 54 | options["device"] = hackrf_board_id_name((hackrf_board_id) board_id); 55 | 56 | char version_str[100]; 57 | 58 | hackrf_version_string_read(device, &version_str[0], 100); 59 | 60 | options["version"] = version_str; 61 | 62 | hackrf_board_partid_serialno_read(device, &read_partid_serialno); 63 | 64 | char part_id_str[100]; 65 | 66 | sprintf(part_id_str, "%08x%08x", read_partid_serialno.part_id[0], read_partid_serialno.part_id[1]); 67 | 68 | options["part_id"] = part_id_str; 69 | 70 | char serial_str[100]; 71 | sprintf(serial_str, "%08x%08x%08x%08x", read_partid_serialno.serial_no[0], 72 | read_partid_serialno.serial_no[1], read_partid_serialno.serial_no[2], 73 | read_partid_serialno.serial_no[3]); 74 | options["serial"] = serial_str; 75 | 76 | //generate a displayable label string with trimmed serial 77 | size_t ofs = 0; 78 | while (ofs < sizeof(serial_str) and serial_str[ofs] == '0') ofs++; 79 | char label_str[100]; 80 | sprintf(label_str, "%s #%d %s", options["device"].c_str(), i, serial_str+ofs); 81 | options["label"] = label_str; 82 | 83 | //filter based on serial and idx 84 | const bool serialMatch = args.count("serial") == 0 or args.at("serial") == options["serial"]; 85 | const bool idxMatch = args.count("hackrf") == 0 or std::stoi(args.at("hackrf")) == i; 86 | if (serialMatch and idxMatch) 87 | { 88 | results.push_back(options); 89 | _cachedResults[serial_str] = options; 90 | } 91 | 92 | hackrf_close(device); 93 | } 94 | 95 | } 96 | 97 | } 98 | 99 | hackrf_device_list_free(list); 100 | 101 | //fill in the cached results for claimed handles 102 | for (const auto &serial : HackRF_getClaimedSerials()) 103 | { 104 | if (_cachedResults.count(serial) == 0) continue; 105 | if (args.count("serial") != 0 and args.at("serial") != serial) continue; 106 | results.push_back(_cachedResults.at(serial)); 107 | } 108 | 109 | return results; 110 | } 111 | 112 | static SoapySDR::Device *make_HackRF(const SoapySDR::Kwargs &args) 113 | { 114 | return new SoapyHackRF(args); 115 | } 116 | 117 | static SoapySDR::Registry register_hackrf("hackrf", &find_HackRF, &make_HackRF, SOAPY_SDR_ABI_VERSION); 118 | -------------------------------------------------------------------------------- /HackRF_Session.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Josh Blum 5 | * Permission is hereby granted, free of charge, to any person obtaining a copy 6 | * of this software and associated documentation files (the "Software"), to deal 7 | * in the Software without restriction, including without limitation the rights 8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | * copies of the Software, and to permit persons to whom the Software is 10 | * furnished to do so, subject to the following conditions: 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | * THE SOFTWARE. 20 | */ 21 | 22 | #include "SoapyHackRF.hpp" 23 | #include 24 | #include 25 | #include 26 | 27 | static std::mutex sessionMutex; 28 | static size_t sessionCount = 0; 29 | 30 | SoapyHackRFSession::SoapyHackRFSession(void) 31 | { 32 | std::lock_guard lock(sessionMutex); 33 | 34 | if (sessionCount == 0) 35 | { 36 | int ret = hackrf_init(); 37 | if (ret != HACKRF_SUCCESS) 38 | { 39 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_init() failed -- %s", hackrf_error_name(hackrf_error(ret))); 40 | } 41 | } 42 | sessionCount++; 43 | } 44 | 45 | SoapyHackRFSession::~SoapyHackRFSession(void) 46 | { 47 | std::lock_guard lock(sessionMutex); 48 | 49 | sessionCount--; 50 | if (sessionCount == 0) 51 | { 52 | int ret = hackrf_exit(); 53 | if (ret != HACKRF_SUCCESS) 54 | { 55 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_exit() failed -- %s", hackrf_error_name(hackrf_error(ret))); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /HackRF_Settings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Wei Jiang 5 | * Copyright (c) 2015-2017 Josh Blum 6 | * Copyright (c) 2017 Kevin Mehall 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include "SoapyHackRF.hpp" 25 | 26 | std::set &HackRF_getClaimedSerials(void) 27 | { 28 | static std::set serials; 29 | return serials; 30 | } 31 | 32 | SoapyHackRF::SoapyHackRF( const SoapySDR::Kwargs &args ) 33 | { 34 | if (args.count("label") != 0) 35 | SoapySDR_logf( SOAPY_SDR_INFO, "Opening %s...", args.at("label").c_str()); 36 | 37 | _rx_stream.vga_gain=16; 38 | _rx_stream.lna_gain=16; 39 | _rx_stream.amp_gain=0; 40 | _rx_stream.frequency=0; 41 | _rx_stream.samplerate=0; 42 | _rx_stream.bandwidth=0; 43 | _rx_stream.overflow = false; 44 | 45 | _tx_stream.vga_gain=0; 46 | _tx_stream.amp_gain=0; 47 | _tx_stream.frequency=0; 48 | _tx_stream.samplerate=0; 49 | _tx_stream.bandwidth=0; 50 | _tx_stream.burst_samps=0; 51 | _tx_stream.burst_end=false; 52 | _tx_stream.underflow = false; 53 | 54 | _current_mode=HACKRF_TRANSCEIVER_MODE_OFF; 55 | 56 | _auto_bandwidth=true; 57 | 58 | _dev = nullptr; 59 | 60 | if (args.count("serial") == 0) 61 | throw std::runtime_error("no hackrf device matches"); 62 | _serial = args.at("serial"); 63 | 64 | _current_amp = 0; 65 | 66 | _current_frequency = 0; 67 | 68 | _current_samplerate = 0; 69 | 70 | _current_bandwidth=0; 71 | 72 | int ret = hackrf_open_by_serial(_serial.c_str(), &_dev); 73 | if ( ret != HACKRF_SUCCESS ) 74 | { 75 | SoapySDR_logf( SOAPY_SDR_INFO, "Could not Open HackRF Device" ); 76 | throw std::runtime_error("hackrf open failed"); 77 | } 78 | 79 | HackRF_getClaimedSerials().insert(_serial); 80 | } 81 | 82 | 83 | SoapyHackRF::~SoapyHackRF( void ) 84 | { 85 | HackRF_getClaimedSerials().erase(_serial); 86 | 87 | if ( _dev ) 88 | { 89 | hackrf_close( _dev ); 90 | } 91 | 92 | /* cleanup device handles */ 93 | } 94 | 95 | 96 | /******************************************************************* 97 | * Identification API 98 | ******************************************************************/ 99 | 100 | std::string SoapyHackRF::getDriverKey( void ) const 101 | { 102 | 103 | return("HackRF"); 104 | } 105 | 106 | 107 | std::string SoapyHackRF::getHardwareKey( void ) const 108 | { 109 | std::lock_guard lock(_device_mutex); 110 | uint8_t board_id=BOARD_ID_INVALID; 111 | 112 | hackrf_board_id_read(_dev,&board_id); 113 | 114 | return(hackrf_board_id_name((hackrf_board_id)board_id)); 115 | } 116 | 117 | 118 | SoapySDR::Kwargs SoapyHackRF::getHardwareInfo( void ) const 119 | { 120 | std::lock_guard lock(_device_mutex); 121 | SoapySDR::Kwargs info; 122 | 123 | char version_str[100]; 124 | 125 | hackrf_version_string_read(_dev, &version_str[0], 100); 126 | 127 | info["version"] = version_str; 128 | 129 | read_partid_serialno_t read_partid_serialno; 130 | 131 | hackrf_board_partid_serialno_read(_dev, &read_partid_serialno); 132 | 133 | char part_id_str[100]; 134 | 135 | sprintf(part_id_str, "%08x%08x", read_partid_serialno.part_id[0], read_partid_serialno.part_id[1]); 136 | 137 | info["part id"] = part_id_str; 138 | 139 | char serial_str[100]; 140 | sprintf(serial_str, "%08x%08x%08x%08x", read_partid_serialno.serial_no[0], read_partid_serialno.serial_no[1], read_partid_serialno.serial_no[2], read_partid_serialno.serial_no[3]); 141 | info["serial"] = serial_str; 142 | 143 | uint16_t clock; 144 | 145 | hackrf_si5351c_read(_dev,0,&clock); 146 | 147 | info["clock source"]=(clock==0x51)?"internal":"external"; 148 | 149 | return(info); 150 | 151 | } 152 | 153 | 154 | /******************************************************************* 155 | * Channels API 156 | ******************************************************************/ 157 | 158 | size_t SoapyHackRF::getNumChannels( const int dir ) const 159 | { 160 | return(1); 161 | } 162 | 163 | 164 | bool SoapyHackRF::getFullDuplex( const int direction, const size_t channel ) const 165 | { 166 | return(false); 167 | } 168 | 169 | /******************************************************************* 170 | * Settings API 171 | ******************************************************************/ 172 | 173 | SoapySDR::ArgInfoList SoapyHackRF::getSettingInfo(void) const 174 | { 175 | SoapySDR::ArgInfoList setArgs; 176 | 177 | SoapySDR::ArgInfo biastxArg; 178 | biastxArg.key="bias_tx"; 179 | biastxArg.value="false"; 180 | biastxArg.name="Antenna Bias"; 181 | biastxArg.description="Antenna port power control."; 182 | biastxArg.type=SoapySDR::ArgInfo::BOOL; 183 | setArgs.push_back(biastxArg); 184 | 185 | return setArgs; 186 | } 187 | 188 | void SoapyHackRF::writeSetting(const std::string &key, const std::string &value) 189 | { 190 | if(key=="bias_tx"){ 191 | std::lock_guard lock(_device_mutex); 192 | _tx_stream.bias=(value=="true") ? true : false; 193 | int ret=hackrf_set_antenna_enable(_dev,_tx_stream.bias); 194 | if(ret!=HACKRF_SUCCESS){ 195 | 196 | SoapySDR_logf(SOAPY_SDR_INFO,"Failed to apply antenna bias voltage"); 197 | 198 | } 199 | } 200 | 201 | } 202 | 203 | std::string SoapyHackRF::readSetting(const std::string &key) const 204 | { 205 | if (key == "bias_tx") { 206 | return _tx_stream.bias?"true":"false"; 207 | } 208 | return ""; 209 | } 210 | /******************************************************************* 211 | * Antenna API 212 | ******************************************************************/ 213 | 214 | std::vector SoapyHackRF::listAntennas( const int direction, const size_t channel ) const 215 | { 216 | std::vector options; 217 | options.push_back( "TX/RX" ); 218 | return(options); 219 | } 220 | 221 | 222 | void SoapyHackRF::setAntenna( const int direction, const size_t channel, const std::string &name ) 223 | { 224 | /* TODO delete this function or throw if name != RX... */ 225 | } 226 | 227 | 228 | std::string SoapyHackRF::getAntenna( const int direction, const size_t channel ) const 229 | { 230 | return("TX/RX"); 231 | } 232 | 233 | 234 | /******************************************************************* 235 | * Frontend corrections API 236 | ******************************************************************/ 237 | 238 | 239 | bool SoapyHackRF::hasDCOffsetMode( const int direction, const size_t channel ) const 240 | { 241 | return(false); 242 | } 243 | 244 | 245 | /******************************************************************* 246 | * Gain API 247 | ******************************************************************/ 248 | 249 | std::vector SoapyHackRF::listGains( const int direction, const size_t channel ) const 250 | { 251 | std::vector options; 252 | if ( direction == SOAPY_SDR_RX ) 253 | { 254 | // in gr-osmosdr/lib/soapy/ soapy_sink_c.cc and soapy_source_c.cc expect if_gain at front and bb_gain at back 255 | options.push_back( "LNA" ); // RX: if_gain 256 | options.push_back( "AMP" ); // RX: rf_gain 257 | options.push_back( "VGA" ); // RX: bb_gain 258 | } 259 | else 260 | { 261 | options.push_back( "VGA" ); // TX: if_gain 262 | options.push_back( "AMP" ); // TX: rf_gain 263 | } 264 | 265 | return(options); 266 | /* 267 | * list available gain elements, 268 | * the functions below have a "name" parameter 269 | */ 270 | } 271 | 272 | 273 | void SoapyHackRF::setGainMode( const int direction, const size_t channel, const bool automatic ) 274 | { 275 | /* enable AGC if the hardware supports it, or remove this function */ 276 | } 277 | 278 | 279 | bool SoapyHackRF::getGainMode( const int direction, const size_t channel ) const 280 | { 281 | return(false); 282 | /* ditto for the AGC */ 283 | } 284 | 285 | 286 | void SoapyHackRF::setGain( const int direction, const size_t channel, const double value ) 287 | { 288 | std::lock_guard lock(_device_mutex); 289 | int32_t ret(0), gain(0); 290 | gain = value; 291 | SoapySDR_logf(SOAPY_SDR_DEBUG,"setGain RF %s, channel %d, gain %d", direction == SOAPY_SDR_RX ? "RX" : "TX", channel, gain); 292 | 293 | if ( direction == SOAPY_SDR_RX ) 294 | { 295 | if ( gain <= 0 ) 296 | { 297 | _rx_stream.lna_gain = 0; 298 | _rx_stream.vga_gain = 0; 299 | _current_amp = 0; 300 | }else if ( gain <= (HACKRF_RX_LNA_MAX_DB / 2) + (HACKRF_RX_VGA_MAX_DB / 2) ) 301 | { 302 | _rx_stream.vga_gain = (gain / 3) & ~0x1; 303 | _rx_stream.lna_gain = gain - _rx_stream.vga_gain; 304 | _current_amp = 0; 305 | }else if ( gain <= ( (HACKRF_RX_LNA_MAX_DB / 2) + (HACKRF_RX_VGA_MAX_DB / 2) + HACKRF_AMP_MAX_DB) ) 306 | { 307 | _current_amp = HACKRF_AMP_MAX_DB; 308 | _rx_stream.vga_gain = ( (gain - _current_amp) / 3) & ~0x1; 309 | _rx_stream.lna_gain = gain -_current_amp - _rx_stream.vga_gain; 310 | }else if ( gain <= HACKRF_RX_LNA_MAX_DB + HACKRF_RX_VGA_MAX_DB + HACKRF_AMP_MAX_DB ) 311 | { 312 | _current_amp = HACKRF_AMP_MAX_DB; 313 | _rx_stream.vga_gain = (gain - _current_amp) * double(HACKRF_RX_LNA_MAX_DB) / double(HACKRF_RX_VGA_MAX_DB); 314 | _rx_stream.lna_gain = gain - _current_amp - _rx_stream.vga_gain; 315 | } 316 | 317 | _rx_stream.amp_gain=_current_amp; 318 | 319 | ret = hackrf_set_lna_gain( _dev, _rx_stream.lna_gain ); 320 | ret |= hackrf_set_vga_gain( _dev, _rx_stream.vga_gain ); 321 | ret |= hackrf_set_amp_enable( _dev, (_current_amp > 0) ? 1 : 0 ); 322 | }else if ( direction == SOAPY_SDR_TX ) 323 | { 324 | if ( gain <= 0 ) 325 | { 326 | _current_amp = 0; 327 | _tx_stream.vga_gain = 0; 328 | }else if ( gain <= (HACKRF_TX_VGA_MAX_DB / 2) ) 329 | { 330 | _current_amp = 0; 331 | _tx_stream.vga_gain = gain; 332 | }else if ( gain <= HACKRF_TX_VGA_MAX_DB + HACKRF_AMP_MAX_DB ) 333 | { 334 | _current_amp = HACKRF_AMP_MAX_DB; 335 | _tx_stream.vga_gain = gain - HACKRF_AMP_MAX_DB; 336 | } 337 | 338 | _tx_stream.amp_gain=_current_amp; 339 | 340 | ret = hackrf_set_txvga_gain( _dev, _tx_stream.vga_gain ); 341 | ret |= hackrf_set_amp_enable( _dev, (_current_amp > 0) ? 1 : 0 ); 342 | } 343 | if ( ret != HACKRF_SUCCESS ) 344 | { 345 | SoapySDR::logf( SOAPY_SDR_ERROR, "setGain(%f) returned %s", value, hackrf_error_name( (hackrf_error) ret ) ); 346 | } 347 | } 348 | 349 | 350 | void SoapyHackRF::setGain( const int direction, const size_t channel, const std::string &name, const double value ) 351 | { 352 | std::lock_guard lock(_device_mutex); 353 | SoapySDR_logf(SOAPY_SDR_DEBUG,"setGain %s %s, channel %d, gain %d", name.c_str(), direction == SOAPY_SDR_RX ? "RX" : "TX", channel, (int)value); 354 | if ( name == "AMP" ) 355 | { 356 | _current_amp = value; 357 | _current_amp = (_current_amp > 0)?HACKRF_AMP_MAX_DB : 0; //clip to possible values 358 | 359 | if(direction == SOAPY_SDR_RX){ 360 | _rx_stream.amp_gain=_current_amp; 361 | }else if (direction ==SOAPY_SDR_TX){ 362 | _tx_stream.amp_gain=_current_amp; 363 | } 364 | 365 | if ( _dev != NULL ) 366 | { 367 | int ret = hackrf_set_amp_enable( _dev, (_current_amp > 0)?1 : 0 ); 368 | if ( ret != HACKRF_SUCCESS ) 369 | { 370 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_amp_enable(%f) returned %s", _current_amp, hackrf_error_name( (hackrf_error) ret ) ); 371 | } 372 | } 373 | }else if ( direction == SOAPY_SDR_RX and name == "LNA" ) 374 | { 375 | _rx_stream.lna_gain = value; 376 | if ( _dev != NULL ) 377 | { 378 | int ret = hackrf_set_lna_gain( _dev, _rx_stream.lna_gain ); 379 | if ( ret != HACKRF_SUCCESS ) 380 | { 381 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_lna_gain(%f) returned %s", _rx_stream.lna_gain, hackrf_error_name( (hackrf_error) ret ) ); 382 | } 383 | } 384 | }else if ( direction == SOAPY_SDR_RX and name == "VGA" ) 385 | { 386 | _rx_stream.vga_gain = value; 387 | if ( _dev != NULL ) 388 | { 389 | int ret = hackrf_set_vga_gain( _dev, _rx_stream.vga_gain ); 390 | if ( ret != HACKRF_SUCCESS ) 391 | { 392 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_vga_gain(%f) returned %s", _rx_stream.vga_gain, hackrf_error_name( (hackrf_error) ret ) ); 393 | } 394 | } 395 | }else if ( direction == SOAPY_SDR_TX and name == "VGA" ) 396 | { 397 | _tx_stream.vga_gain = value; 398 | if ( _dev != NULL ) 399 | { 400 | int ret = hackrf_set_txvga_gain( _dev, _tx_stream.vga_gain ); 401 | if ( ret != HACKRF_SUCCESS ) 402 | { 403 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_txvga_gain(%f) returned %s", _tx_stream.vga_gain, hackrf_error_name( (hackrf_error) ret ) ); 404 | } 405 | } 406 | } 407 | 408 | 409 | /* set individual gain element by name */ 410 | } 411 | 412 | 413 | double SoapyHackRF::getGain( const int direction, const size_t channel, const std::string &name ) const 414 | { 415 | std::lock_guard lock(_device_mutex); 416 | double gain = 0.0; 417 | if ( direction == SOAPY_SDR_RX and name == "AMP" ) 418 | { 419 | gain = _rx_stream.amp_gain; 420 | }else if ( direction == SOAPY_SDR_TX and name == "AMP" ) 421 | { 422 | gain = _tx_stream.amp_gain; 423 | }else if ( direction == SOAPY_SDR_RX and name == "LNA" ) 424 | { 425 | gain = _rx_stream.lna_gain; 426 | }else if ( direction == SOAPY_SDR_RX and name == "VGA" ) 427 | { 428 | gain = _rx_stream.vga_gain; 429 | }else if ( direction == SOAPY_SDR_TX and name == "VGA" ) 430 | { 431 | gain = _tx_stream.vga_gain; 432 | } 433 | 434 | return(gain); 435 | } 436 | 437 | 438 | SoapySDR::Range SoapyHackRF::getGainRange( const int direction, const size_t channel, const std::string &name ) const 439 | { 440 | if ( name == "AMP" ) 441 | return(SoapySDR::Range( 0, HACKRF_AMP_MAX_DB, HACKRF_AMP_MAX_DB) ); 442 | if ( direction == SOAPY_SDR_RX and name == "LNA" ) 443 | return(SoapySDR::Range( 0, HACKRF_RX_LNA_MAX_DB, 8.0 ) ); 444 | if ( direction == SOAPY_SDR_RX and name == "VGA") 445 | return(SoapySDR::Range( 0, HACKRF_RX_VGA_MAX_DB, 2.0 ) ); 446 | if ( direction == SOAPY_SDR_TX and name == "VGA" ) 447 | return(SoapySDR::Range( 0, HACKRF_TX_VGA_MAX_DB, 1.0 ) ); 448 | return(SoapySDR::Range( 0, 0 ) ); 449 | } 450 | 451 | 452 | /******************************************************************* 453 | * Frequency API 454 | ******************************************************************/ 455 | 456 | void SoapyHackRF::setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args ) 457 | { 458 | if ( name == "BB" ) 459 | return; 460 | if ( name != "RF" ) 461 | throw std::runtime_error( "setFrequency(" + name + ") unknown name" ); 462 | 463 | std::lock_guard lock(_device_mutex); 464 | _current_frequency = frequency; 465 | 466 | 467 | if(direction==SOAPY_SDR_RX){ 468 | 469 | _rx_stream.frequency=_current_frequency; 470 | } 471 | if(direction==SOAPY_SDR_TX){ 472 | 473 | _tx_stream.frequency=_current_frequency; 474 | } 475 | 476 | if ( _dev != NULL ) 477 | { 478 | int ret = hackrf_set_freq( _dev, _current_frequency ); 479 | 480 | if ( ret != HACKRF_SUCCESS ) 481 | { 482 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_freq(%f) returned %s", _current_frequency, hackrf_error_name( (hackrf_error) ret ) ); 483 | } 484 | } 485 | } 486 | 487 | 488 | double SoapyHackRF::getFrequency( const int direction, const size_t channel, const std::string &name ) const 489 | { 490 | if ( name == "BB" ) 491 | return(0.0); 492 | if ( name != "RF" ) 493 | throw std::runtime_error( "getFrequency(" + name + ") unknown name" ); 494 | 495 | std::lock_guard lock(_device_mutex); 496 | double freq(0.0); 497 | 498 | if(direction==SOAPY_SDR_RX){ 499 | 500 | freq = _rx_stream.frequency; 501 | } 502 | if(direction==SOAPY_SDR_TX){ 503 | 504 | freq = _tx_stream.frequency; 505 | } 506 | return(freq); 507 | } 508 | 509 | SoapySDR::ArgInfoList SoapyHackRF::getFrequencyArgsInfo(const int direction, const size_t channel) const 510 | { 511 | SoapySDR::ArgInfoList freqArgs; 512 | // TODO: frequency arguments 513 | return freqArgs; 514 | } 515 | 516 | std::vector SoapyHackRF::listFrequencies( const int direction, const size_t channel ) const 517 | { 518 | std::vector names; 519 | names.push_back( "RF" ); 520 | return(names); 521 | } 522 | 523 | 524 | SoapySDR::RangeList SoapyHackRF::getFrequencyRange( const int direction, const size_t channel, const std::string &name ) const 525 | { 526 | if ( name == "BB" ) 527 | return(SoapySDR::RangeList( 1, SoapySDR::Range( 0.0, 0.0 ) ) ); 528 | if ( name != "RF" ) 529 | throw std::runtime_error( "getFrequencyRange(" + name + ") unknown name" ); 530 | return(SoapySDR::RangeList( 1, SoapySDR::Range( 0, 7250000000ull ) ) ); 531 | } 532 | 533 | 534 | /******************************************************************* 535 | * Sample Rate API 536 | ******************************************************************/ 537 | 538 | void SoapyHackRF::setSampleRate( const int direction, const size_t channel, const double rate ) 539 | { 540 | std::lock_guard lock(_device_mutex); 541 | _current_samplerate = rate; 542 | 543 | if(direction==SOAPY_SDR_RX){ 544 | 545 | _rx_stream.samplerate=_current_samplerate; 546 | } 547 | if(direction==SOAPY_SDR_TX){ 548 | 549 | _tx_stream.samplerate=_current_samplerate; 550 | } 551 | 552 | if ( _dev != NULL ) 553 | { 554 | int ret = hackrf_set_sample_rate( _dev, _current_samplerate ); 555 | 556 | if ( ret != HACKRF_SUCCESS ) 557 | { 558 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_sample_rate(%f) returned %s", _current_samplerate, hackrf_error_name( (hackrf_error) ret ) ); 559 | throw std::runtime_error( "setSampleRate()" ); 560 | } 561 | } 562 | } 563 | 564 | 565 | double SoapyHackRF::getSampleRate( const int direction, const size_t channel ) const 566 | { 567 | std::lock_guard lock(_device_mutex); 568 | double samp(0.0); 569 | if(direction==SOAPY_SDR_RX){ 570 | 571 | samp= _rx_stream.samplerate; 572 | } 573 | if(direction==SOAPY_SDR_TX){ 574 | 575 | samp= _tx_stream.samplerate; 576 | } 577 | 578 | return(samp); 579 | } 580 | 581 | 582 | std::vector SoapyHackRF::listSampleRates( const int direction, const size_t channel ) const 583 | { 584 | std::vector options; 585 | for ( double r = 1e6; r <= 20e6; r += 1e6 ) 586 | { 587 | options.push_back( r ); 588 | } 589 | return(options); 590 | } 591 | 592 | 593 | void SoapyHackRF::setBandwidth( const int direction, const size_t channel, const double bw ) 594 | { 595 | std::lock_guard lock(_device_mutex); 596 | _current_bandwidth = bw; 597 | 598 | if(direction==SOAPY_SDR_RX){ 599 | 600 | _rx_stream.bandwidth=_current_bandwidth; 601 | } 602 | if(direction==SOAPY_SDR_TX){ 603 | 604 | _tx_stream.bandwidth=_current_bandwidth; 605 | } 606 | 607 | if(_current_bandwidth > 0){ 608 | _auto_bandwidth=false; 609 | 610 | if ( _dev != NULL ) 611 | { 612 | int ret = hackrf_set_baseband_filter_bandwidth( _dev, _current_bandwidth ); 613 | if ( ret != HACKRF_SUCCESS ) 614 | { 615 | SoapySDR::logf( SOAPY_SDR_ERROR, "hackrf_set_baseband_filter_bandwidth(%f) returned %s", _current_bandwidth, hackrf_error_name( (hackrf_error) ret ) ); 616 | throw std::runtime_error( "setBandwidth()" ); 617 | } 618 | } 619 | 620 | }else{ 621 | _auto_bandwidth=true; 622 | } 623 | 624 | } 625 | 626 | 627 | double SoapyHackRF::getBandwidth( const int direction, const size_t channel ) const 628 | { 629 | std::lock_guard lock(_device_mutex); 630 | double bw(0.0); 631 | if(direction==SOAPY_SDR_RX){ 632 | 633 | bw = _rx_stream.bandwidth; 634 | } 635 | if(direction==SOAPY_SDR_TX){ 636 | 637 | bw = _tx_stream.bandwidth; 638 | } 639 | 640 | return (bw); 641 | } 642 | 643 | 644 | std::vector SoapyHackRF::listBandwidths( const int direction, const size_t channel ) const 645 | { 646 | std::vector options; 647 | options.push_back( 1750000 ); 648 | options.push_back( 2500000 ); 649 | options.push_back( 3500000 ); 650 | options.push_back( 5000000 ); 651 | options.push_back( 5500000 ); 652 | options.push_back( 6000000 ); 653 | options.push_back( 7000000 ); 654 | options.push_back( 8000000 ); 655 | options.push_back( 9000000 ); 656 | options.push_back( 10000000 ); 657 | options.push_back( 12000000 ); 658 | options.push_back( 14000000 ); 659 | options.push_back( 15000000 ); 660 | options.push_back( 20000000 ); 661 | options.push_back( 24000000 ); 662 | options.push_back( 28000000 ); 663 | return(options); 664 | } 665 | -------------------------------------------------------------------------------- /HackRF_Streaming.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015-2016 Wei Jiang 5 | * Copyright (c) 2015-2017 Josh Blum 6 | * Copyright (c) 2017 Kevin Mehall 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #include "SoapyHackRF.hpp" 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include //min 30 | 31 | int _hackrf_rx_callback( hackrf_transfer *transfer ) 32 | { 33 | SoapyHackRF* obj = (SoapyHackRF *) transfer->rx_ctx; 34 | return(obj->hackrf_rx_callback( (int8_t *) transfer->buffer, transfer->valid_length ) ); 35 | } 36 | 37 | 38 | int _hackrf_tx_callback( hackrf_transfer *transfer ) 39 | { 40 | SoapyHackRF* obj = (SoapyHackRF *) transfer->tx_ctx; 41 | return(obj->hackrf_tx_callback( (int8_t *) transfer->buffer, transfer->valid_length ) ); 42 | } 43 | 44 | int SoapyHackRF::hackrf_rx_callback( int8_t *buffer, int32_t length ) 45 | { 46 | std::unique_lock lock(_buf_mutex); 47 | _rx_stream.buf_tail = (_rx_stream.buf_head + _rx_stream.buf_count) % _rx_stream.buf_num; 48 | memcpy(_rx_stream.buf[_rx_stream.buf_tail], buffer, length ); 49 | 50 | if ( _rx_stream.buf_count == _rx_stream.buf_num ) 51 | { 52 | _rx_stream.overflow=true; 53 | _rx_stream.buf_head = (_rx_stream.buf_head + 1) % _rx_stream.buf_num; 54 | }else { 55 | _rx_stream.buf_count++; 56 | } 57 | _buf_cond.notify_one(); 58 | 59 | return(0); 60 | } 61 | 62 | 63 | int SoapyHackRF::hackrf_tx_callback( int8_t *buffer, int32_t length ) 64 | { 65 | std::unique_lock lock(_buf_mutex); 66 | if ( _tx_stream.buf_count == 0 ) 67 | { 68 | memset( buffer, 0, length ); 69 | _tx_stream.underflow=true; 70 | }else { 71 | memcpy( buffer, _tx_stream.buf[_tx_stream.buf_tail], length ); 72 | _tx_stream.buf_tail = (_tx_stream.buf_tail + 1) % _tx_stream.buf_num; 73 | 74 | _tx_stream.buf_count--; 75 | 76 | if(_tx_stream.burst_end) 77 | { 78 | _tx_stream.burst_samps -= (length/BYTES_PER_SAMPLE); 79 | if(_tx_stream.burst_samps < 0 ) { 80 | _tx_stream.burst_end = false; 81 | _tx_stream.burst_samps = 0; 82 | return -1; 83 | } 84 | } 85 | } 86 | _buf_cond.notify_one(); 87 | 88 | return(0); 89 | } 90 | 91 | std::vector SoapyHackRF::getStreamFormats(const int direction, const size_t channel) const 92 | { 93 | std::vector formats; 94 | 95 | formats.push_back(SOAPY_SDR_CS8); 96 | formats.push_back(SOAPY_SDR_CS16); 97 | formats.push_back(SOAPY_SDR_CF32); 98 | formats.push_back(SOAPY_SDR_CF64); 99 | 100 | return formats; 101 | } 102 | 103 | std::string SoapyHackRF::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const 104 | { 105 | fullScale = 128; 106 | return SOAPY_SDR_CS8; 107 | } 108 | 109 | SoapySDR::ArgInfoList SoapyHackRF::getStreamArgsInfo(const int direction, const size_t channel) const 110 | { 111 | SoapySDR::ArgInfoList streamArgs; 112 | 113 | SoapySDR::ArgInfo buffersArg; 114 | buffersArg.key="buffers"; 115 | buffersArg.value = std::to_string(BUF_NUM); 116 | buffersArg.name = "Buffer Count"; 117 | buffersArg.description = "Number of buffers per read."; 118 | buffersArg.units = "buffers"; 119 | buffersArg.type = SoapySDR::ArgInfo::INT; 120 | streamArgs.push_back(buffersArg); 121 | 122 | return streamArgs; 123 | } 124 | 125 | void SoapyHackRF::Stream::allocate_buffers() { 126 | buf = (int8_t * *) malloc( buf_num * sizeof(int8_t *) ); 127 | if ( buf ) { 128 | for ( unsigned int i = 0; i < buf_num; ++i ) { 129 | buf[i] = (int8_t *) malloc( buf_len ); 130 | } 131 | } 132 | } 133 | 134 | void SoapyHackRF::Stream::clear_buffers() { 135 | if ( buf ) { 136 | for ( unsigned int i = 0; i < buf_num; ++i ) { 137 | if ( buf[i] ) { 138 | free( buf[i] ); 139 | } 140 | } 141 | free( buf ); 142 | buf = NULL; 143 | } 144 | 145 | buf_count = 0; 146 | buf_tail = 0; 147 | buf_head = 0; 148 | remainderSamps = 0; 149 | remainderOffset = 0; 150 | remainderBuff = nullptr; 151 | remainderHandle = -1; 152 | } 153 | 154 | SoapySDR::Stream *SoapyHackRF::setupStream( 155 | const int direction, 156 | const std::string &format, 157 | const std::vector &channels, 158 | const SoapySDR::Kwargs &args ) 159 | { 160 | std::lock_guard lock(_device_mutex); 161 | 162 | if ( channels.size() > 1 or( channels.size() > 0 and channels.at( 0 ) != 0 ) ) 163 | { 164 | throw std::runtime_error( "setupStream invalid channel selection" ); 165 | } 166 | 167 | if(direction==SOAPY_SDR_RX){ 168 | if (_rx_stream.opened) { 169 | throw std::runtime_error("RX stream already opened"); 170 | } 171 | 172 | if ( format == SOAPY_SDR_CS8 ) 173 | { 174 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS8." ); 175 | _rx_stream.format = HACKRF_FORMAT_INT8; 176 | }else if ( format == SOAPY_SDR_CS16 ) 177 | { 178 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS16." ); 179 | _rx_stream.format = HACKRF_FORMAT_INT16; 180 | }else if ( format == SOAPY_SDR_CF32 ) 181 | { 182 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF32." ); 183 | _rx_stream.format= HACKRF_FORMAT_FLOAT32; 184 | }else if(format==SOAPY_SDR_CF64){ 185 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF64." ); 186 | _rx_stream.format= HACKRF_FORMAT_FLOAT64; 187 | }else throw std::runtime_error( "setupStream invalid format " + format ); 188 | 189 | _rx_stream.buf_num = BUF_NUM; 190 | 191 | if ( args.count( "buffers" ) != 0 ) 192 | { 193 | try 194 | { 195 | int numBuffers_in = std::stoi(args.at("buffers")); 196 | if (numBuffers_in > 0) { 197 | _rx_stream.buf_num = numBuffers_in; 198 | } 199 | } 200 | catch (const std::invalid_argument &){} 201 | 202 | } 203 | _rx_stream.allocate_buffers(); 204 | 205 | _rx_stream.opened = true; 206 | 207 | return RX_STREAM; 208 | } else if(direction==SOAPY_SDR_TX){ 209 | if (_tx_stream.opened) { 210 | throw std::runtime_error("TX stream already opened"); 211 | } 212 | 213 | if ( format == SOAPY_SDR_CS8 ) 214 | { 215 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS8." ); 216 | _tx_stream.format = HACKRF_FORMAT_INT8; 217 | }else if ( format == SOAPY_SDR_CS16 ) 218 | { 219 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS16." ); 220 | _tx_stream.format = HACKRF_FORMAT_INT16; 221 | }else if ( format == SOAPY_SDR_CF32 ) 222 | { 223 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF32." ); 224 | _tx_stream.format= HACKRF_FORMAT_FLOAT32; 225 | }else if(format==SOAPY_SDR_CF64){ 226 | SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF64." ); 227 | _tx_stream.format= HACKRF_FORMAT_FLOAT64; 228 | }else throw std::runtime_error( "setupStream invalid format " + format ); 229 | 230 | _tx_stream.buf_num = BUF_NUM; 231 | 232 | if ( args.count( "buffers" ) != 0 ) 233 | { 234 | try 235 | { 236 | int numBuffers_in = std::stoi(args.at("buffers")); 237 | if (numBuffers_in > 0) 238 | { 239 | _tx_stream.buf_num = numBuffers_in; 240 | } 241 | } 242 | catch (const std::invalid_argument &){} 243 | 244 | } 245 | 246 | _tx_stream.allocate_buffers(); 247 | _tx_stream.opened = true; 248 | 249 | return TX_STREAM; 250 | } else { 251 | throw std::runtime_error("Invalid direction"); 252 | } 253 | } 254 | 255 | void SoapyHackRF::closeStream( SoapySDR::Stream *stream ) 256 | { 257 | this->deactivateStream(stream, 0, 0); 258 | std::lock_guard lock(_device_mutex); 259 | if (stream == RX_STREAM) { 260 | _rx_stream.clear_buffers(); 261 | _rx_stream.opened = false; 262 | } else if (stream == TX_STREAM) { 263 | _tx_stream.clear_buffers(); 264 | _tx_stream.opened = false; 265 | } 266 | } 267 | 268 | 269 | size_t SoapyHackRF::getStreamMTU( SoapySDR::Stream *stream ) const 270 | { 271 | if(stream == RX_STREAM){ 272 | return _rx_stream.buf_len/BYTES_PER_SAMPLE; 273 | } else if(stream == TX_STREAM){ 274 | return _tx_stream.buf_len/BYTES_PER_SAMPLE; 275 | } else { 276 | throw std::runtime_error("Invalid stream"); 277 | } 278 | } 279 | 280 | int SoapyHackRF::activateStream( 281 | SoapySDR::Stream *stream, 282 | const int flags, 283 | const long long timeNs, 284 | const size_t numElems ) 285 | { 286 | 287 | if(stream == RX_STREAM){ 288 | 289 | std::lock_guard lock(_device_mutex); 290 | 291 | 292 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_RX) 293 | return 0; 294 | 295 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_TX){ 296 | 297 | if(_tx_stream.burst_end){ 298 | 299 | while(hackrf_is_streaming(_dev)==HACKRF_TRUE) 300 | std::this_thread::sleep_for(std::chrono::milliseconds(10)); 301 | } 302 | 303 | hackrf_stop_tx(_dev); 304 | 305 | // determine what (if any) settings need to be changed for RX; only applicable if there is both a source and sink block 306 | // sample_rate 307 | if(_current_samplerate != _rx_stream.samplerate) { 308 | _current_samplerate = _rx_stream.samplerate; 309 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set RX samplerate to %f", _current_samplerate); 310 | hackrf_set_sample_rate(_dev,_current_samplerate); 311 | } 312 | 313 | // frequency 314 | if(_current_frequency != _rx_stream.frequency) { 315 | _current_frequency = _rx_stream.frequency; 316 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set RX frequency to %lu", _current_frequency); 317 | hackrf_set_freq(_dev,_current_frequency); 318 | } 319 | 320 | // frequency_correction; assume RX and TX use the same correction 321 | // This will be the setting of whichever block was last added to the flow graph 322 | 323 | // RF Gain (RF Amp for TX & RX) 324 | if(_current_amp != _rx_stream.amp_gain) { 325 | _current_amp = _rx_stream.amp_gain; 326 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set RX amp gain to %d", _current_amp); 327 | hackrf_set_amp_enable(_dev,(_current_amp > 0)?1 : 0 ); 328 | } 329 | 330 | // IF Gain (LNA for RX; VGA_TX for TX) 331 | // BB Gain (VGA for RX; n/a for TX) 332 | // These are independant values in the hackrf, so no need to change 333 | 334 | // Bandwidth 335 | if(_current_bandwidth !=_rx_stream.bandwidth) { 336 | _current_bandwidth =_rx_stream.bandwidth; 337 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set RX bandwidth to %d", _current_bandwidth); 338 | hackrf_set_baseband_filter_bandwidth(_dev,_current_bandwidth); 339 | } 340 | } 341 | 342 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Start RX"); 343 | 344 | //reset buffer tracking before streaming 345 | { 346 | _rx_stream.buf_count = 0; 347 | _rx_stream.buf_head = 0; 348 | _rx_stream.buf_tail = 0; 349 | } 350 | 351 | int ret = hackrf_start_rx(_dev, _hackrf_rx_callback, (void *) this); 352 | if (ret != HACKRF_SUCCESS) { 353 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_start_rx() failed -- %s", hackrf_error_name(hackrf_error(ret))); 354 | } 355 | 356 | ret=hackrf_is_streaming(_dev); 357 | 358 | if (ret==HACKRF_ERROR_STREAMING_EXIT_CALLED){ 359 | 360 | hackrf_close(_dev); 361 | hackrf_open_by_serial(_serial.c_str(), &_dev); 362 | _current_frequency=_rx_stream.frequency; 363 | hackrf_set_freq(_dev,_current_frequency); 364 | _current_samplerate=_rx_stream.samplerate; 365 | hackrf_set_sample_rate(_dev,_current_samplerate); 366 | _current_bandwidth=_rx_stream.bandwidth; 367 | hackrf_set_baseband_filter_bandwidth(_dev,_current_bandwidth); 368 | _current_amp=_rx_stream.amp_gain; 369 | hackrf_set_amp_enable(_dev,(_current_amp > 0)?1 : 0 ); 370 | hackrf_set_lna_gain(_dev,_rx_stream.lna_gain); 371 | hackrf_set_vga_gain(_dev,_rx_stream.vga_gain); 372 | hackrf_start_rx(_dev,_hackrf_rx_callback,(void *) this); 373 | ret=hackrf_is_streaming(_dev); 374 | } 375 | if(ret!=HACKRF_TRUE){ 376 | SoapySDR_logf(SOAPY_SDR_ERROR,"Activate RX Stream Failed."); 377 | return SOAPY_SDR_STREAM_ERROR; 378 | 379 | } 380 | _current_mode = HACKRF_TRANSCEIVER_MODE_RX; 381 | 382 | } else if (stream == TX_STREAM) { 383 | 384 | std::lock_guard lock(_device_mutex); 385 | 386 | if((flags & SOAPY_SDR_END_BURST)!=0 and numElems!=0) { 387 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_RX){ 388 | _tx_stream.buf_head=0; 389 | _tx_stream.buf_tail=0; 390 | _tx_stream.burst_end = true; 391 | _tx_stream.burst_samps = numElems; 392 | } 393 | } 394 | 395 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_TX) 396 | return 0; 397 | 398 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_RX){ 399 | 400 | hackrf_stop_rx(_dev); 401 | 402 | // determine what (if any) settings need to be changed for TX; only applicable if there is both a source and sink block 403 | // sample_rate 404 | if(_current_samplerate != _tx_stream.samplerate) { 405 | _current_samplerate=_tx_stream.samplerate; 406 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set TX samplerate to %f", _current_samplerate); 407 | hackrf_set_sample_rate(_dev,_current_samplerate); 408 | } 409 | 410 | // frequency 411 | if(_current_frequency != _tx_stream.frequency) { 412 | _current_frequency=_tx_stream.frequency; 413 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set TX frequency to %lu", _current_frequency); 414 | hackrf_set_freq(_dev,_current_frequency); 415 | } 416 | 417 | // frequency_correction; assume RX and TX use the same correction 418 | // This will be the setting of whichever block was last added to the flow graph 419 | 420 | // RF Gain (RF Amp for TX & RX) 421 | if(_current_amp != _tx_stream.amp_gain) { 422 | _current_amp=_tx_stream.amp_gain; 423 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set TX amp gain to %d", _current_amp); 424 | hackrf_set_amp_enable(_dev,(_current_amp > 0)?1 : 0 ); 425 | } 426 | 427 | // IF Gain (LNA for RX, VGA_TX for TX) 428 | // BB Gain (VGA for RX, n/a for TX) 429 | // These are independant values in the hackrf, so no need to change 430 | 431 | // Bandwidth 432 | if(_current_bandwidth !=_tx_stream.bandwidth) { 433 | _current_bandwidth =_tx_stream.bandwidth; 434 | SoapySDR_logf(SOAPY_SDR_DEBUG, "activateStream - Set RX bandwidth to %d", _current_bandwidth); 435 | hackrf_set_baseband_filter_bandwidth(_dev,_current_bandwidth); 436 | } 437 | 438 | } 439 | 440 | SoapySDR_logf( SOAPY_SDR_DEBUG, "Start TX" ); 441 | 442 | int ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *) this ); 443 | if (ret != HACKRF_SUCCESS) 444 | { 445 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_start_tx() failed -- %s", hackrf_error_name(hackrf_error(ret))); 446 | } 447 | 448 | ret=hackrf_is_streaming(_dev); 449 | 450 | if (ret==HACKRF_ERROR_STREAMING_EXIT_CALLED){ 451 | 452 | 453 | hackrf_close(_dev); 454 | hackrf_open_by_serial(_serial.c_str(), &_dev); 455 | _current_frequency=_tx_stream.frequency; 456 | hackrf_set_freq(_dev,_current_frequency); 457 | _current_samplerate=_tx_stream.samplerate; 458 | hackrf_set_sample_rate(_dev,_current_samplerate); 459 | _current_bandwidth=_tx_stream.bandwidth; 460 | hackrf_set_baseband_filter_bandwidth(_dev,_current_bandwidth); 461 | _current_amp=_rx_stream.amp_gain; 462 | hackrf_set_amp_enable(_dev,(_current_amp > 0)?1 : 0 ); 463 | hackrf_set_txvga_gain(_dev,_tx_stream.vga_gain); 464 | hackrf_set_antenna_enable(_dev,_tx_stream.bias); 465 | hackrf_start_tx(_dev,_hackrf_tx_callback,(void *) this); 466 | ret=hackrf_is_streaming(_dev); 467 | } 468 | if(ret!=HACKRF_TRUE){ 469 | 470 | SoapySDR_logf(SOAPY_SDR_ERROR,"Activate TX Stream Failed."); 471 | return SOAPY_SDR_STREAM_ERROR; 472 | } 473 | _current_mode = HACKRF_TRANSCEIVER_MODE_TX; 474 | 475 | } 476 | 477 | return(0); 478 | } 479 | 480 | 481 | int SoapyHackRF::deactivateStream( 482 | SoapySDR::Stream *stream, 483 | const int flags, 484 | const long long timeNs ) 485 | { 486 | 487 | if(stream == RX_STREAM){ 488 | 489 | std::lock_guard lock(_device_mutex); 490 | 491 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_RX) { 492 | 493 | int ret = hackrf_stop_rx(_dev); 494 | if (ret != HACKRF_SUCCESS) { 495 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_stop_rx() failed -- %s", hackrf_error_name(hackrf_error(ret))); 496 | } 497 | _current_mode = HACKRF_TRANSCEIVER_MODE_OFF; 498 | } 499 | } else if(stream == TX_STREAM) { 500 | 501 | std::lock_guard lock(_device_mutex); 502 | 503 | if(_current_mode==HACKRF_TRANSCEIVER_MODE_TX) { 504 | int ret = hackrf_stop_tx(_dev); 505 | if (ret != HACKRF_SUCCESS) { 506 | SoapySDR::logf(SOAPY_SDR_ERROR, "hackrf_stop_tx() failed -- %s", hackrf_error_name(hackrf_error(ret))); 507 | } 508 | _current_mode = HACKRF_TRANSCEIVER_MODE_OFF; 509 | } 510 | 511 | } 512 | return(0); 513 | } 514 | 515 | void readbuf(int8_t * src, void * dst, uint32_t len,uint32_t format,size_t offset){ 516 | 517 | if(format==HACKRF_FORMAT_INT8){ 518 | int8_t *samples_cs8=(int8_t *) dst+offset*BYTES_PER_SAMPLE; 519 | for (uint32_t i=0;i> 8); 561 | dst[i*BYTES_PER_SAMPLE+1] = (int8_t) (samples_cs16[i*BYTES_PER_SAMPLE+1] >> 8); 562 | } 563 | }else if(format==HACKRF_FORMAT_FLOAT32){ 564 | float *samples_cf32=(float *) src+offset*BYTES_PER_SAMPLE; 565 | for (uint32_t i=0;igetStreamMTU(stream)); 596 | 597 | size_t samp_avail=0; 598 | 599 | if(_rx_stream.remainderHandle >= 0){ 600 | 601 | const size_t n =std::min(_rx_stream.remainderSamps,returnedElems); 602 | 603 | if(nreleaseReadBuffer(stream,_rx_stream.remainderHandle); 615 | _rx_stream.remainderHandle=-1; 616 | _rx_stream.remainderOffset=0; 617 | } 618 | 619 | if(n==returnedElems) 620 | return returnedElems; 621 | } 622 | 623 | size_t handle; 624 | int ret = this->acquireReadBuffer(stream, handle, (const void **)&_rx_stream.remainderBuff, flags, timeNs, timeoutUs); 625 | 626 | if (ret < 0){ 627 | if((ret == SOAPY_SDR_TIMEOUT) && (samp_avail > 0)){ 628 | return samp_avail; 629 | } 630 | return ret; 631 | } 632 | 633 | _rx_stream.remainderHandle=handle; 634 | _rx_stream.remainderSamps=ret; 635 | 636 | 637 | const size_t n =std::min((returnedElems-samp_avail),_rx_stream.remainderSamps); 638 | 639 | readbuf(_rx_stream.remainderBuff,buffs[0],n,_rx_stream.format,samp_avail); 640 | _rx_stream.remainderSamps -=n; 641 | _rx_stream.remainderOffset +=n; 642 | 643 | if(_rx_stream.remainderSamps==0){ 644 | this->releaseReadBuffer(stream,_rx_stream.remainderHandle); 645 | _rx_stream.remainderHandle=-1; 646 | _rx_stream.remainderOffset=0; 647 | } 648 | 649 | return(returnedElems); 650 | } 651 | 652 | 653 | int SoapyHackRF::writeStream( 654 | SoapySDR::Stream *stream, 655 | const void * const *buffs, 656 | const size_t numElems, 657 | int &flags, 658 | const long long timeNs, 659 | const long timeoutUs ) 660 | { 661 | if(stream != TX_STREAM){ 662 | return SOAPY_SDR_NOT_SUPPORTED; 663 | } 664 | 665 | size_t returnedElems = std::min(numElems,this->getStreamMTU(stream)); 666 | 667 | size_t samp_avail = 0; 668 | 669 | if(_tx_stream.remainderHandle>=0){ 670 | 671 | const size_t n =std::min(_tx_stream.remainderSamps,returnedElems); 672 | 673 | if(nreleaseWriteBuffer(stream,_tx_stream.remainderHandle,_tx_stream.remainderOffset,flags,timeNs); 683 | _tx_stream.remainderHandle=-1; 684 | _tx_stream.remainderOffset=0; 685 | } 686 | 687 | if(n==returnedElems) 688 | return returnedElems; 689 | 690 | } 691 | 692 | size_t handle; 693 | 694 | int ret=this->acquireWriteBuffer(stream,handle,(void **)&_tx_stream.remainderBuff,timeoutUs); 695 | if(ret < 0){ 696 | if((ret == SOAPY_SDR_TIMEOUT) && (samp_avail > 0)){ 697 | return samp_avail; 698 | } 699 | return ret; 700 | } 701 | 702 | _tx_stream.remainderHandle=handle; 703 | _tx_stream.remainderSamps=ret; 704 | 705 | const size_t n =std::min((returnedElems-samp_avail),_tx_stream.remainderSamps); 706 | 707 | writebuf(buffs[0],_tx_stream.remainderBuff,n,_tx_stream.format,samp_avail); 708 | _tx_stream.remainderSamps -=n; 709 | _tx_stream.remainderOffset +=n; 710 | 711 | if(_tx_stream.remainderSamps==0){ 712 | this->releaseWriteBuffer(stream,_tx_stream.remainderHandle,_tx_stream.remainderOffset,flags,timeNs); 713 | _tx_stream.remainderHandle=-1; 714 | _tx_stream.remainderOffset=0; 715 | } 716 | 717 | return returnedElems; 718 | 719 | } 720 | 721 | 722 | int SoapyHackRF::readStreamStatus( 723 | SoapySDR::Stream *stream, 724 | size_t &chanMask, 725 | int &flags, 726 | long long &timeNs, 727 | const long timeoutUs 728 | ){ 729 | 730 | if(stream != TX_STREAM){ 731 | return SOAPY_SDR_NOT_SUPPORTED; 732 | } 733 | 734 | //calculate when the loop should exit 735 | const auto timeout = std::chrono::duration_cast(std::chrono::microseconds(timeoutUs)); 736 | const auto exitTime = std::chrono::high_resolution_clock::now() + timeout; 737 | 738 | //poll for status events until the timeout expires 739 | while (true) 740 | { 741 | if(_tx_stream.underflow){ 742 | _tx_stream.underflow=false; 743 | SoapySDR::log(SOAPY_SDR_SSI, "U"); 744 | return SOAPY_SDR_UNDERFLOW; 745 | } 746 | 747 | //sleep for a fraction of the total timeout 748 | const auto sleepTimeUs = std::min(1000, timeoutUs/10); 749 | std::this_thread::sleep_for(std::chrono::microseconds(sleepTimeUs)); 750 | 751 | //check for timeout expired 752 | const auto timeNow = std::chrono::high_resolution_clock::now(); 753 | if (exitTime < timeNow) return SOAPY_SDR_TIMEOUT; 754 | } 755 | } 756 | 757 | int SoapyHackRF::acquireReadBuffer( 758 | SoapySDR::Stream *stream, 759 | size_t &handle, 760 | const void **buffs, 761 | int &flags, 762 | long long &timeNs, 763 | const long timeoutUs) 764 | { 765 | 766 | 767 | if(stream != RX_STREAM){ 768 | return SOAPY_SDR_NOT_SUPPORTED; 769 | } 770 | 771 | if ( _current_mode!=HACKRF_TRANSCEIVER_MODE_RX ) { 772 | 773 | //wait for tx to be consumed before switching 774 | { 775 | std::unique_lock lock( _buf_mutex ); 776 | if (not _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs), 777 | [this]{return this->_tx_stream.buf_count == 0;})) return SOAPY_SDR_TIMEOUT; 778 | } 779 | 780 | int ret=this->activateStream(stream); 781 | if(ret<0) return ret; 782 | } 783 | 784 | std::unique_lock lock( _buf_mutex ); 785 | 786 | while (_rx_stream.buf_count == 0) 787 | { 788 | _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs)); 789 | if (_rx_stream.buf_count == 0) return SOAPY_SDR_TIMEOUT; 790 | } 791 | 792 | if(_rx_stream.overflow) { 793 | flags|=SOAPY_SDR_END_ABRUPT; 794 | _rx_stream.overflow=false; 795 | SoapySDR::log(SOAPY_SDR_SSI,"O"); 796 | return SOAPY_SDR_OVERFLOW; 797 | } 798 | 799 | handle=_rx_stream.buf_head; 800 | _rx_stream.buf_head = (_rx_stream.buf_head + 1) % _rx_stream.buf_num; 801 | this->getDirectAccessBufferAddrs(stream,handle,(void **)buffs); 802 | 803 | return this->getStreamMTU(stream); 804 | } 805 | 806 | void SoapyHackRF::releaseReadBuffer( 807 | SoapySDR::Stream *stream, 808 | const size_t handle) 809 | { 810 | if(stream != RX_STREAM){ 811 | throw std::runtime_error("Invalid stream"); 812 | } 813 | 814 | std::unique_lock lock( _buf_mutex ); 815 | _rx_stream.buf_count--; 816 | } 817 | 818 | int SoapyHackRF::acquireWriteBuffer( 819 | SoapySDR::Stream *stream, 820 | size_t &handle, 821 | void **buffs, 822 | const long timeoutUs) 823 | { 824 | 825 | if(stream != TX_STREAM){ 826 | return SOAPY_SDR_NOT_SUPPORTED; 827 | } 828 | 829 | if(_current_mode!=HACKRF_TRANSCEIVER_MODE_TX) { 830 | int ret=this->activateStream(stream); 831 | if(ret<0) return ret; 832 | } 833 | 834 | std::unique_lock lock( _buf_mutex ); 835 | 836 | while ( _tx_stream.buf_count == _tx_stream.buf_num ) 837 | { 838 | _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs)); 839 | if (_tx_stream.buf_count == _tx_stream.buf_num) return SOAPY_SDR_TIMEOUT; 840 | } 841 | 842 | handle=_tx_stream.buf_head; 843 | _tx_stream.buf_head = (_tx_stream.buf_head + 1) % _tx_stream.buf_num; 844 | 845 | this->getDirectAccessBufferAddrs(stream,handle,buffs); 846 | 847 | if(_tx_stream.burst_end){ 848 | if((_tx_stream.burst_samps - int32_t(this->getStreamMTU(stream))) < 0){ 849 | memset(buffs[0],0,this->getStreamMTU(stream)); 850 | return _tx_stream.burst_samps; 851 | } 852 | } 853 | return this->getStreamMTU(stream); 854 | 855 | } 856 | 857 | void SoapyHackRF::releaseWriteBuffer( 858 | SoapySDR::Stream *stream, 859 | const size_t handle, 860 | const size_t numElems, 861 | int &flags, 862 | const long long timeNs) 863 | { 864 | if (stream == TX_STREAM) { 865 | std::unique_lock lock( _buf_mutex ); 866 | _tx_stream.buf_count++; 867 | } else { 868 | throw std::runtime_error("Invalid stream"); 869 | } 870 | } 871 | 872 | size_t SoapyHackRF::getNumDirectAccessBuffers( 873 | SoapySDR::Stream *stream) 874 | { 875 | if (stream == RX_STREAM) { 876 | return _rx_stream.buf_num; 877 | } else if(stream == TX_STREAM){ 878 | return _tx_stream.buf_num; 879 | } else { 880 | throw std::runtime_error("Invalid stream"); 881 | } 882 | } 883 | 884 | int SoapyHackRF::getDirectAccessBufferAddrs( 885 | SoapySDR::Stream *stream, 886 | const size_t handle, 887 | void **buffs) 888 | { 889 | 890 | if (stream == RX_STREAM) { 891 | buffs[0]=(void *)_rx_stream.buf[handle]; 892 | } else if (stream == TX_STREAM) { 893 | buffs[0]=(void *)_tx_stream.buf[handle]; 894 | } else { 895 | throw std::runtime_error("Invalid stream"); 896 | } 897 | 898 | return 0; 899 | } 900 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Soapy SDR module for Hack RF 2 | 3 | ## Build Status 4 | 5 | - Travis: [![Travis Build Status](https://travis-ci.org/pothosware/SoapyHackRF.svg?branch=master)](https://travis-ci.org/pothosware/SoapyHackRF) 6 | 7 | ## Dependencies 8 | 9 | * SoapySDR - https://github.com/pothosware/SoapySDR/wiki 10 | * libhackrf - https://github.com/mossmann/hackrf/wiki 11 | 12 | ## Documentation 13 | 14 | * https://github.com/pothosware/SoapyHackRF/wiki 15 | 16 | ## Licensing information 17 | 18 | The MIT License (MIT) 19 | 20 | Copyright (c) 2015 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining a copy 23 | of this software and associated documentation files (the "Software"), to deal 24 | in the Software without restriction, including without limitation the rights 25 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 26 | copies of the Software, and to permit persons to whom the Software is 27 | furnished to do so, subject to the following conditions: 28 | 29 | The above copyright notice and this permission notice shall be included in 30 | all copies or substantial portions of the Software. 31 | 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 36 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 37 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 38 | THE SOFTWARE. 39 | -------------------------------------------------------------------------------- /SoapyHackRF.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Wei Jiang 5 | * Copyright (c) 2015-2017 Josh Blum 6 | * Copyright (c) 2017 Kevin Mehall 7 | * Permission is hereby granted, free of charge, to any person obtaining a copy 8 | * of this software and associated documentation files (the "Software"), to deal 9 | * in the Software without restriction, including without limitation the rights 10 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | * copies of the Software, and to permit persons to whom the Software is 12 | * furnished to do so, subject to the following conditions: 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | * THE SOFTWARE. 22 | */ 23 | 24 | #pragma once 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #define BUF_LEN 262144 34 | #define BUF_NUM 15 35 | #define BYTES_PER_SAMPLE 2 36 | #define HACKRF_RX_VGA_MAX_DB 62 37 | #define HACKRF_TX_VGA_MAX_DB 47 38 | #define HACKRF_RX_LNA_MAX_DB 40 39 | #define HACKRF_AMP_MAX_DB 14 40 | 41 | enum HackRF_Format { 42 | HACKRF_FORMAT_FLOAT32 =0, 43 | HACKRF_FORMAT_INT16 =1, 44 | HACKRF_FORMAT_INT8 =2, 45 | HACKRF_FORMAT_FLOAT64 =3, 46 | }; 47 | 48 | typedef enum { 49 | HACKRF_TRANSCEIVER_MODE_OFF = 0, 50 | HACKRF_TRANSCEIVER_MODE_RX = 1, 51 | HACKRF_TRANSCEIVER_MODE_TX = 2, 52 | } HackRF_transceiver_mode_t; 53 | 54 | std::set &HackRF_getClaimedSerials(void); 55 | 56 | /*! 57 | * The session object manages hackrf_init/exit 58 | * with a process-wide reference count. 59 | */ 60 | class SoapyHackRFSession 61 | { 62 | public: 63 | SoapyHackRFSession(void); 64 | ~SoapyHackRFSession(void); 65 | }; 66 | 67 | class SoapyHackRF : public SoapySDR::Device 68 | { 69 | public: 70 | SoapyHackRF( const SoapySDR::Kwargs & args ); 71 | 72 | ~SoapyHackRF( void ); 73 | 74 | 75 | /******************************************************************* 76 | * Identification API 77 | ******************************************************************/ 78 | 79 | std::string getDriverKey( void ) const; 80 | 81 | 82 | std::string getHardwareKey( void ) const; 83 | 84 | 85 | SoapySDR::Kwargs getHardwareInfo( void ) const; 86 | 87 | 88 | /******************************************************************* 89 | * Channels API 90 | ******************************************************************/ 91 | 92 | size_t getNumChannels( const int ) const; 93 | 94 | 95 | bool getFullDuplex( const int direction, const size_t channel ) const; 96 | 97 | 98 | /******************************************************************* 99 | * Stream API 100 | ******************************************************************/ 101 | 102 | std::vector getStreamFormats(const int direction, const size_t channel) const; 103 | 104 | std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; 105 | 106 | SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; 107 | 108 | SoapySDR::Stream *setupStream( 109 | const int direction, 110 | const std::string &format, 111 | const std::vector &channels = std::vector(), 112 | const SoapySDR::Kwargs &args = SoapySDR::Kwargs() ); 113 | 114 | 115 | void closeStream( SoapySDR::Stream *stream ); 116 | 117 | 118 | size_t getStreamMTU( SoapySDR::Stream *stream ) const; 119 | 120 | 121 | int activateStream( 122 | SoapySDR::Stream *stream, 123 | const int flags = 0, 124 | const long long timeNs = 0, 125 | const size_t numElems = 0 ); 126 | 127 | 128 | int deactivateStream( 129 | SoapySDR::Stream *stream, 130 | const int flags = 0, 131 | const long long timeNs = 0 ); 132 | 133 | 134 | int readStream( 135 | SoapySDR::Stream *stream, 136 | void * const *buffs, 137 | const size_t numElems, 138 | int &flags, 139 | long long &timeNs, 140 | const long timeoutUs = 100000 ); 141 | 142 | 143 | int writeStream( 144 | SoapySDR::Stream *stream, 145 | const void * const *buffs, 146 | const size_t numElems, 147 | int &flags, 148 | const long long timeNs = 0, 149 | const long timeoutUs = 100000); 150 | 151 | int readStreamStatus( 152 | SoapySDR::Stream *stream, 153 | size_t &chanMask, 154 | int &flags, 155 | long long &timeNs, 156 | const long timeoutUs 157 | ); 158 | 159 | 160 | int acquireReadBuffer( 161 | SoapySDR::Stream *stream, 162 | size_t &handle, 163 | const void **buffs, 164 | int &flags, 165 | long long &timeNs, 166 | const long timeoutUs = 100000); 167 | 168 | void releaseReadBuffer( 169 | SoapySDR::Stream *stream, 170 | const size_t handle); 171 | 172 | int acquireWriteBuffer( 173 | SoapySDR::Stream *stream, 174 | size_t &handle, 175 | void **buffs, 176 | const long timeoutUs = 100000); 177 | 178 | void releaseWriteBuffer( 179 | SoapySDR::Stream *stream, 180 | const size_t handle, 181 | const size_t numElems, 182 | int &flags, 183 | const long long timeNs = 0); 184 | 185 | size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); 186 | 187 | int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); 188 | 189 | /******************************************************************* 190 | * Settings API 191 | ******************************************************************/ 192 | 193 | SoapySDR::ArgInfoList getSettingInfo(void) const; 194 | 195 | 196 | void writeSetting(const std::string &key, const std::string &value); 197 | 198 | 199 | std::string readSetting(const std::string &key) const; 200 | 201 | 202 | /******************************************************************* 203 | * Antenna API 204 | ******************************************************************/ 205 | 206 | std::vector listAntennas( const int direction, const size_t channel ) const; 207 | 208 | 209 | void setAntenna( const int direction, const size_t channel, const std::string &name ); 210 | 211 | 212 | std::string getAntenna( const int direction, const size_t channel ) const; 213 | 214 | 215 | /******************************************************************* 216 | * Frontend corrections API 217 | ******************************************************************/ 218 | 219 | bool hasDCOffsetMode( const int direction, const size_t channel ) const; 220 | 221 | 222 | /******************************************************************* 223 | * Gain API 224 | ******************************************************************/ 225 | 226 | std::vector listGains( const int direction, const size_t channel ) const; 227 | 228 | 229 | void setGainMode( const int direction, const size_t channel, const bool automatic ); 230 | 231 | 232 | bool getGainMode( const int direction, const size_t channel ) const; 233 | 234 | 235 | void setGain( const int direction, const size_t channel, const double value ); 236 | 237 | 238 | void setGain( const int direction, const size_t channel, const std::string &name, const double value ); 239 | 240 | 241 | double getGain( const int direction, const size_t channel, const std::string &name ) const; 242 | 243 | 244 | SoapySDR::Range getGainRange( const int direction, const size_t channel, const std::string &name ) const; 245 | 246 | 247 | /******************************************************************* 248 | * Frequency API 249 | ******************************************************************/ 250 | 251 | void setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args = SoapySDR::Kwargs() ); 252 | 253 | 254 | double getFrequency( const int direction, const size_t channel, const std::string &name ) const; 255 | 256 | 257 | SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; 258 | 259 | 260 | std::vector listFrequencies( const int direction, const size_t channel ) const; 261 | 262 | 263 | SoapySDR::RangeList getFrequencyRange( const int direction, const size_t channel, const std::string &name ) const; 264 | 265 | 266 | /******************************************************************* 267 | * Sample Rate API 268 | ******************************************************************/ 269 | 270 | void setSampleRate( const int direction, const size_t channel, const double rate ); 271 | 272 | 273 | double getSampleRate( const int direction, const size_t channel ) const; 274 | 275 | 276 | std::vector listSampleRates( const int direction, const size_t channel ) const; 277 | 278 | 279 | void setBandwidth( const int direction, const size_t channel, const double bw ); 280 | 281 | 282 | double getBandwidth( const int direction, const size_t channel ) const; 283 | 284 | 285 | std::vector listBandwidths( const int direction, const size_t channel ) const; 286 | 287 | /******************************************************************* 288 | * HackRF callback 289 | ******************************************************************/ 290 | int hackrf_tx_callback( int8_t *buffer, int32_t length ); 291 | 292 | 293 | int hackrf_rx_callback( int8_t *buffer, int32_t length ); 294 | 295 | 296 | 297 | 298 | private: 299 | 300 | SoapySDR::Stream* const TX_STREAM = (SoapySDR::Stream*) 0x1; 301 | SoapySDR::Stream* const RX_STREAM = (SoapySDR::Stream*) 0x2; 302 | 303 | struct Stream { 304 | Stream(): opened(false), buf_num(BUF_NUM), buf_len(BUF_LEN), buf(nullptr), 305 | buf_head(0), buf_tail(0), buf_count(0), 306 | remainderHandle(-1), remainderSamps(0), remainderOffset(0), remainderBuff(nullptr), 307 | format(HACKRF_FORMAT_INT8) {} 308 | 309 | bool opened; 310 | uint32_t buf_num; 311 | uint32_t buf_len; 312 | int8_t **buf; 313 | uint32_t buf_head; 314 | uint32_t buf_tail; 315 | uint32_t buf_count; 316 | 317 | int32_t remainderHandle; 318 | size_t remainderSamps; 319 | size_t remainderOffset; 320 | int8_t* remainderBuff; 321 | uint32_t format; 322 | 323 | ~Stream() { clear_buffers(); } 324 | void clear_buffers(); 325 | void allocate_buffers(); 326 | }; 327 | 328 | struct RXStream: Stream { 329 | uint32_t vga_gain; 330 | uint32_t lna_gain; 331 | uint8_t amp_gain; 332 | double samplerate; 333 | uint32_t bandwidth; 334 | uint64_t frequency; 335 | 336 | bool overflow; 337 | }; 338 | 339 | struct TXStream: Stream { 340 | uint32_t vga_gain; 341 | uint8_t amp_gain; 342 | double samplerate; 343 | uint32_t bandwidth; 344 | uint64_t frequency; 345 | bool bias; 346 | 347 | bool underflow; 348 | 349 | bool burst_end; 350 | int32_t burst_samps; 351 | } ; 352 | 353 | RXStream _rx_stream; 354 | TXStream _tx_stream; 355 | 356 | bool _auto_bandwidth; 357 | 358 | hackrf_device * _dev; 359 | std::string _serial; 360 | 361 | uint64_t _current_frequency; 362 | 363 | double _current_samplerate; 364 | 365 | uint32_t _current_bandwidth; 366 | 367 | uint8_t _current_amp; 368 | 369 | /// Mutex protecting all use of the hackrf device _dev and other instance variables. 370 | /// Most of the hackrf API is thread-safe because it only calls libusb, however 371 | /// the activateStream() method in this library can close and re-open the device, 372 | /// so all use of _dev must be protected 373 | mutable std::mutex _device_mutex; 374 | std::mutex _buf_mutex; 375 | std::condition_variable _buf_cond; 376 | 377 | HackRF_transceiver_mode_t _current_mode; 378 | 379 | SoapyHackRFSession _sess; 380 | }; 381 | -------------------------------------------------------------------------------- /cmake/cmake_uninstall.cmake.in: -------------------------------------------------------------------------------- 1 | if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 2 | message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 3 | endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") 4 | 5 | file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) 6 | string(REGEX REPLACE "\n" ";" files "${files}") 7 | foreach(file ${files}) 8 | message(STATUS "Uninstalling $ENV{DESTDIR}${file}") 9 | if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 10 | exec_program( 11 | "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" 12 | OUTPUT_VARIABLE rm_out 13 | RETURN_VALUE rm_retval 14 | ) 15 | if(NOT "${rm_retval}" STREQUAL 0) 16 | message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") 17 | endif(NOT "${rm_retval}" STREQUAL 0) 18 | else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 19 | message(STATUS "File $ENV{DESTDIR}${file} does not exist.") 20 | endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") 21 | endforeach(file) 22 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | soapyhackrf (0.3.4-1) unstable; urgency=low 2 | 3 | * Release 0.3.4 (2022-01-16) 4 | 5 | -- Josh Blum Sun, 16 Jan 2022 12:18:02 -0000 6 | 7 | soapyhackrf (0.3.3-1) unstable; urgency=low 8 | 9 | * Release 0.3.3 (2018-05-09) 10 | 11 | -- Josh Blum Wed, 09 May 2018 20:16:07 -0000 12 | 13 | soapyhackrf (0.3.2-1) unstable; urgency=low 14 | 15 | * Release 0.3.2 (2017-11-19) 16 | 17 | -- Josh Blum Sun, 19 Nov 2017 15:39:14 -0000 18 | 19 | soapyhackrf (0.3.1-1) unstable; urgency=low 20 | 21 | * Release 0.3.1 (2017-06-19) 22 | 23 | -- Josh Blum Mon, 19 Jun 2017 20:18:21 -0000 24 | 25 | soapyhackrf (0.3.0-1) unstable; urgency=low 26 | 27 | * Release 0.3.0 (2017-04-29) 28 | 29 | -- Josh Blum Sat, 29 Apr 2017 15:04:55 -0000 30 | 31 | soapyhackrf (0.2.2) unstable; urgency=low 32 | 33 | * Release 0.2.2 (2016-10-19) 34 | 35 | -- Josh Blum Wed, 19 Oct 2016 11:25:17 -0700 36 | 37 | soapyhackrf (0.2.1) unstable; urgency=low 38 | 39 | * Release 0.2.1 (2016-02-29) 40 | 41 | -- Josh Blum Mon, 29 Feb 2016 13:19:38 -0800 42 | 43 | soapyhackrf (0.2.0) unstable; urgency=low 44 | 45 | * Release 0.2.0 (2015-11-20) 46 | 47 | -- Josh Blum Fri, 23 Oct 2015 18:39:30 -0700 48 | 49 | soapyhackrf (0.1.0) unstable; urgency=low 50 | 51 | * Release 0.1.0 (2015-10-10) 52 | 53 | -- Josh Blum Sat, 10 Oct 2015 11:34:51 -0700 54 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: soapyhackrf 2 | Section: libs 3 | Priority: optional 4 | Maintainer: jocover 5 | Uploaders: Josh Blum 6 | Build-Depends: 7 | debhelper (>= 9.0.0), 8 | cmake, 9 | libhackrf-dev, 10 | libsoapysdr-dev 11 | Standards-Version: 4.5.0 12 | Homepage: https://github.com/pothosware/SoapyHackRF/wiki 13 | Vcs-Git: https://github.com/pothosware/SoapyHackRF.git 14 | Vcs-Browser: https://github.com/pothosware/SoapyHackRF 15 | 16 | Package: soapysdr0.7-module-hackrf 17 | Architecture: any 18 | Multi-Arch: same 19 | Depends: ${shlibs:Depends}, ${misc:Depends} 20 | Description: Soapy HackRF - HackRF device support for Soapy SDR. 21 | A Soapy module that supports HackRF devices within the Soapy API. 22 | 23 | Package: soapysdr-module-hackrf 24 | Architecture: all 25 | Depends: soapysdr0.7-module-hackrf, ${misc:Depends} 26 | Description: Soapy HackRF - HackRF device support for Soapy SDR. 27 | A Soapy module that supports HackRF devices within the Soapy API. 28 | . 29 | This is an empty dependency package that pulls in the HackRF module 30 | for the default version of libsoapysdr. 31 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: soapyhackrf 3 | Source: https://github.com/pothosware/SoapyHackRF/wiki 4 | 5 | Files: * 6 | Copyright: 7 | Copyright (c) 2015-2016 Wei Jiang 8 | Copyright (c) 2015-2017 Josh Blum 9 | Copyright (c) 2017 Kevin Mehall 10 | License: MIT 11 | Permission is hereby granted, free of charge, to any person obtaining a copy 12 | of this software and associated documentation files (the "Software"), to deal 13 | in the Software without restriction, including without limitation the rights 14 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 15 | copies of the Software, and to permit persons to whom the Software is 16 | furnished to do so, subject to the following conditions: 17 | . 18 | The above copyright notice and this permission notice shall be included in 19 | all copies or substantial portions of the Software. 20 | . 21 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 26 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 27 | THE SOFTWARE. 28 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | # -*- makefile -*- 3 | 4 | DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) 5 | export DEB_HOST_MULTIARCH 6 | 7 | # Uncomment this to turn on verbose mode. 8 | #export DH_VERBOSE=1 9 | 10 | %: 11 | dh $@ --buildsystem=cmake --parallel 12 | 13 | override_dh_auto_configure: 14 | dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)" 15 | 16 | override_dh_installchangelogs: 17 | dh_installchangelogs Changelog.txt 18 | -------------------------------------------------------------------------------- /debian/soapysdr0.7-module-hackrf.install: -------------------------------------------------------------------------------- 1 | usr/lib/* 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /self_test.py: -------------------------------------------------------------------------------- 1 | import SoapySDR 2 | from SoapySDR import * #SOAPY_SDR_* constants 3 | import numpy as np 4 | import time 5 | 6 | if __name__ == "__main__": 7 | hackrf = SoapySDR.Device(dict(driver="hackrf")) 8 | print hackrf 9 | 10 | hackrf.setSampleRate(SOAPY_SDR_RX, 0, 8e6) 11 | hackrf.setSampleRate(SOAPY_SDR_TX, 0, 8e6) 12 | 13 | """ 14 | for i in range(5): 15 | print(" Make rx stream #%d"%i) 16 | rxStream = hackrf.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0]) 17 | for j in range(5): 18 | numSampsTotal = 10000 19 | print(" Activate, get %d samples, Deactivate #%d"%(numSampsTotal, j)) 20 | hackrf.activateStream(rxStream) 21 | buff = np.array([0]*1024, np.complex64) 22 | while numSampsTotal > 0: 23 | sr = hackrf.readStream(rxStream, [buff], buff.size, timeoutUs=int(1e6)) 24 | #print sr 25 | assert(sr.ret > 0) 26 | numSampsTotal -= sr.ret 27 | hackrf.deactivateStream(rxStream) 28 | hackrf.closeStream(rxStream) 29 | 30 | for i in range(5): 31 | print(" Make tx stream #%d"%i) 32 | txStream = hackrf.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0]) 33 | for j in range(5): 34 | numSampsTotal = 10000 35 | print(" Activate, send %d samples, Deactivate #%d"%(numSampsTotal, j)) 36 | hackrf.activateStream(txStream) 37 | buff = np.array([0]*1024, np.complex64) 38 | while numSampsTotal != 0: 39 | size = min(buff.size, numSampsTotal) 40 | sr = hackrf.writeStream(txStream, [buff], size) 41 | #print sr 42 | if not (sr.ret > 0): print("Fail %s, %d"%(sr, numSampsTotal)) 43 | assert(sr.ret > 0) 44 | numSampsTotal -= sr.ret 45 | hackrf.deactivateStream(txStream) 46 | hackrf.closeStream(txStream) 47 | """ 48 | 49 | #################################################################### 50 | #setup both streams at once 51 | #################################################################### 52 | rxStream = hackrf.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0]) 53 | txStream = hackrf.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0]) 54 | 55 | hackrf.activateStream(rxStream) 56 | hackrf.activateStream(txStream) 57 | 58 | numSampsTotal = 10000 59 | hackrf.activateStream(rxStream) 60 | buff = np.array([0]*1024, np.complex64) 61 | while numSampsTotal > 0: 62 | sr = hackrf.readStream(rxStream, [buff], buff.size, timeoutUs=int(1e6)) 63 | #print sr 64 | assert(sr.ret > 0) 65 | numSampsTotal -= sr.ret 66 | 67 | numSampsTotal = 10000 68 | buff = np.array([0]*1024, np.complex64) 69 | while numSampsTotal != 0: 70 | size = min(buff.size, numSampsTotal) 71 | sr = hackrf.writeStream(txStream, [buff], size) 72 | #print sr 73 | if not (sr.ret > 0): print("Fail %s, %d"%(sr, numSampsTotal)) 74 | assert(sr.ret > 0) 75 | numSampsTotal -= sr.ret 76 | 77 | hackrf.deactivateStream(rxStream) 78 | hackrf.deactivateStream(txStream) 79 | 80 | hackrf.closeStream(rxStream) 81 | hackrf.closeStream(txStream) 82 | --------------------------------------------------------------------------------