├── .gitignore ├── CMakeLists.txt ├── Changelog.txt ├── FindLibAIRSPYHF.cmake ├── LICENSE.txt ├── LibFindMacros.cmake ├── README.md ├── Registration.cpp ├── Settings.cpp ├── SoapyAirspyHF.hpp ├── Streaming.cpp ├── cmake └── cmake_uninstall.cmake.in └── debian ├── changelog ├── compat ├── control ├── copyright ├── docs ├── rules ├── soapysdr0.7-module-airspyhf.install └── source └── format /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################### 2 | # Build Soapy SDR support module for Airspy Devices 3 | ################################################### 4 | 5 | cmake_minimum_required(VERSION 2.8.7) 6 | include(CheckFunctionExists) 7 | 8 | project(SoapyAirspyHF CXX) 9 | 10 | find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) 11 | if (NOT SoapySDR_FOUND) 12 | message(FATAL_ERROR "Soapy SDR development files not found...") 13 | endif () 14 | 15 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) 16 | 17 | find_package(LibAIRSPYHF) 18 | 19 | include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LIBAIRSPYHF_INCLUDE_DIRS}) 20 | 21 | #enable c++11 features 22 | if(CMAKE_COMPILER_IS_GNUCXX) 23 | #C++11 is a required language feature for this project 24 | include(CheckCXXCompilerFlag) 25 | CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11) 26 | if(HAS_STD_CXX11) 27 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") 28 | else(HAS_STD_CXX11) 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") 30 | endif() 31 | #Thread support enabled (not the same as -lpthread) 32 | list(APPEND AIRSPYHF_LIBS -pthread) 33 | #disable warnings for unused parameters 34 | add_definitions(-Wno-unused-parameter) 35 | endif(CMAKE_COMPILER_IS_GNUCXX) 36 | 37 | if (APPLE) 38 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wc++11-extensions") 39 | endif(APPLE) 40 | 41 | # IF (APPLE) 42 | # ADD_DEFINITIONS( 43 | # -D__MACOSX_CORE__ 44 | # ) 45 | # FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) 46 | #SET (AIRSPYHF_LIBS ${COREFOUNDATION_LIBRARY} ${AIRSPYHF_LIBS} ) 47 | # ENDIF (APPLE) 48 | 49 | list(APPEND AIRSPYHF_LIBS ${LIBAIRSPYHF_LIBRARIES}) 50 | 51 | SOAPY_SDR_MODULE_UTIL( 52 | TARGET airspyhfSupport 53 | SOURCES 54 | SoapyAirspyHF.hpp 55 | Registration.cpp 56 | Settings.cpp 57 | Streaming.cpp 58 | LIBRARIES 59 | ${AIRSPYHF_LIBS} 60 | ) 61 | 62 | set(CMAKE_REQUIRED_INCLUDES libairspy/airspyhf.h) 63 | set(CMAKE_REQUIRED_LIBRARIES -lairspyhf) 64 | check_function_exists(airspyhf_set_hf_att HAVE_GAIN_API) 65 | if (${HAVE_GAIN_API}) 66 | message(STATUS "Enabling gain control.") 67 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DHASGAINS=1") 68 | endif() 69 | 70 | ######################################################################## 71 | # uninstall target 72 | ######################################################################## 73 | add_custom_target(uninstall 74 | "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") 75 | configure_file( 76 | "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" 77 | "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" 78 | IMMEDIATE @ONLY) 79 | 80 | -------------------------------------------------------------------------------- /Changelog.txt: -------------------------------------------------------------------------------- 1 | Release 0.2.0 (2021-06-02) 2 | ========================== 3 | 4 | - Fix return early when readStream() not active 5 | - Support opening the device by serial convention 6 | - Use converter registry for sample format support 7 | - Fix return code handling for airspyhf_list_devices() 8 | - Manual gain settings in firmware R1.3 and airspyhf 1.1.2 or later 9 | 10 | Release 0.1.0 (2018-01-12) 11 | ========================== 12 | 13 | - Initial release of Soapy AirspyHF+ support module, based on the Soapy 14 | support module for the original Airspy 15 | -------------------------------------------------------------------------------- /FindLibAIRSPYHF.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(FindPkgConfig) 2 | PKG_CHECK_MODULES(PC_LIBAIRSPYHF libairspyhf) 3 | 4 | FIND_PATH( 5 | LIBAIRSPYHF_INCLUDE_DIRS 6 | NAMES libairspyhf/airspyhf.h 7 | HINTS $ENV{LIBAIRSPY_DIR}/include 8 | ${PC_LIBAIRSPYHF_INCLUDEDIR} 9 | PATHS /usr/local/include 10 | /usr/include 11 | ) 12 | 13 | FIND_LIBRARY( 14 | LIBAIRSPYHF_LIBRARIES 15 | NAMES airspyhf 16 | HINTS $ENV{LIBAIRSPYHF_DIR}/lib 17 | ${PC_LIBAIRSPYHF_LIBDIR} 18 | PATHS /usr/local/lib 19 | /usr/lib 20 | ) 21 | 22 | INCLUDE(FindPackageHandleStandardArgs) 23 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBAIRSPYHF DEFAULT_MSG LIBAIRSPYHF_LIBRARIES LIBAIRSPYHF_INCLUDE_DIRS) 24 | MARK_AS_ADVANCED(LIBAIRSPYHF_LIBRARIES LIBAIRSPYHF_INCLUDE_DIRS) 25 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Charles J. Cliffe 4 | Copyright (c) 2018 Corey Stotts 5 | 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 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /LibFindMacros.cmake: -------------------------------------------------------------------------------- 1 | # Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments 2 | # used for the current package. For this to work, the first parameter must be the 3 | # prefix of the current package, then the prefix of the new package etc, which are 4 | # passed to find_package. 5 | macro (libfind_package PREFIX) 6 | set (LIBFIND_PACKAGE_ARGS ${ARGN}) 7 | if (${PREFIX}_FIND_QUIETLY) 8 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) 9 | endif (${PREFIX}_FIND_QUIETLY) 10 | if (${PREFIX}_FIND_REQUIRED) 11 | set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) 12 | endif (${PREFIX}_FIND_REQUIRED) 13 | find_package(${LIBFIND_PACKAGE_ARGS}) 14 | endmacro (libfind_package) 15 | 16 | # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) 17 | # where they added pkg_check_modules. Consequently I need to support both in my scripts 18 | # to avoid those deprecated warnings. Here's a helper that does just that. 19 | # Works identically to pkg_check_modules, except that no checks are needed prior to use. 20 | macro (libfind_pkg_check_modules PREFIX PKGNAME) 21 | if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 22 | include(UsePkgConfig) 23 | pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) 24 | else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 25 | find_package(PkgConfig) 26 | if (PKG_CONFIG_FOUND) 27 | pkg_check_modules(${PREFIX} ${PKGNAME}) 28 | endif (PKG_CONFIG_FOUND) 29 | endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) 30 | endmacro (libfind_pkg_check_modules) 31 | 32 | # Do the final processing once the paths have been detected. 33 | # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain 34 | # all the variables, each of which contain one include directory. 35 | # Ditto for ${PREFIX}_PROCESS_LIBS and library files. 36 | # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. 37 | # Also handles errors in case library detection was required, etc. 38 | macro (libfind_process PREFIX) 39 | # Skip processing if already processed during this run 40 | if (NOT ${PREFIX}_FOUND) 41 | # Start with the assumption that the library was found 42 | set (${PREFIX}_FOUND TRUE) 43 | 44 | # Process all includes and set _FOUND to false if any are missing 45 | foreach (i ${${PREFIX}_PROCESS_INCLUDES}) 46 | if (${i}) 47 | set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) 48 | mark_as_advanced(${i}) 49 | else (${i}) 50 | set (${PREFIX}_FOUND FALSE) 51 | endif (${i}) 52 | endforeach (i) 53 | 54 | # Process all libraries and set _FOUND to false if any are missing 55 | foreach (i ${${PREFIX}_PROCESS_LIBS}) 56 | if (${i}) 57 | set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) 58 | mark_as_advanced(${i}) 59 | else (${i}) 60 | set (${PREFIX}_FOUND FALSE) 61 | endif (${i}) 62 | endforeach (i) 63 | 64 | # Print message and/or exit on fatal error 65 | if (${PREFIX}_FOUND) 66 | if (NOT ${PREFIX}_FIND_QUIETLY) 67 | message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") 68 | endif (NOT ${PREFIX}_FIND_QUIETLY) 69 | else (${PREFIX}_FOUND) 70 | if (${PREFIX}_FIND_REQUIRED) 71 | foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) 72 | message("${i}=${${i}}") 73 | endforeach (i) 74 | message (FATAL_ERROR "Required library ${PREFIX} NOT FOUND.\nInstall the library (dev version) and try again. If the library is already installed, use ccmake to set the missing variables manually.") 75 | endif (${PREFIX}_FIND_REQUIRED) 76 | endif (${PREFIX}_FOUND) 77 | endif (NOT ${PREFIX}_FOUND) 78 | endmacro (libfind_process) 79 | 80 | macro(libfind_library PREFIX basename) 81 | set(TMP "") 82 | if(MSVC80) 83 | set(TMP -vc80) 84 | endif(MSVC80) 85 | if(MSVC90) 86 | set(TMP -vc90) 87 | endif(MSVC90) 88 | set(${PREFIX}_LIBNAMES ${basename}${TMP}) 89 | if(${ARGC} GREATER 2) 90 | set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) 91 | string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) 92 | set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) 93 | endif(${ARGC} GREATER 2) 94 | find_library(${PREFIX}_LIBRARY 95 | NAMES ${${PREFIX}_LIBNAMES} 96 | PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} 97 | ) 98 | endmacro(libfind_library) 99 | 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Soapy SDR plugin for AirspyHF+ 2 | 3 | ## Dependencies 4 | 5 | * SoapySDR - https://github.com/pothosware/SoapySDR/wiki 6 | * libairspyhf - https://github.com/airspy/airspyhf 7 | 8 | ## Documentation 9 | 10 | * https://github.com/pothosware/SoapyAirspyHF/wiki 11 | 12 | Turning AGC on/off, LNA and RF gain settings requires firmware R1.3 or later 13 | and airspyhf 1.1.2 or later. -------------------------------------------------------------------------------- /Registration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Charles J. Cliffe 5 | * Copyright (c) 2018 Corey Stotts 6 | 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 | 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #include "SoapyAirspyHF.hpp" 27 | #include 28 | #include //malloc 29 | #include 30 | 31 | static std::vector findAirspyHF(const SoapySDR::Kwargs &args) 32 | { 33 | std::vector results; 34 | 35 | airspyhf_lib_version_t asVersion; 36 | airspyhf_lib_version(&asVersion); 37 | 38 | // SoapySDR_setLogLevel(SOAPY_SDR_DEBUG); 39 | 40 | SoapySDR_logf(SOAPY_SDR_DEBUG, "AirSpyHF Lib v%d.%d rev %d", asVersion.major_version, asVersion.minor_version, asVersion.revision); 41 | 42 | uint64_t serials[MAX_DEVICES]; 43 | int count = airspyhf_list_devices(serials, MAX_DEVICES); 44 | if (count == AIRSPYHF_ERROR) { 45 | SoapySDR_logf(SOAPY_SDR_ERROR, "libairspyhf error listing devices"); 46 | return results; 47 | } 48 | 49 | SoapySDR_logf(SOAPY_SDR_DEBUG, "%d AirSpy boards found.", count); 50 | 51 | for (int i = 0; i < count; i++) { 52 | std::stringstream serialstr; 53 | 54 | serialstr.str(""); 55 | serialstr << std::hex << serials[i]; 56 | 57 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Serial %s", serialstr.str().c_str()); 58 | 59 | SoapySDR::Kwargs soapyInfo; 60 | 61 | soapyInfo["label"] = "AirSpy HF+ [" + serialstr.str() + "]"; 62 | soapyInfo["serial"] = serialstr.str(); 63 | 64 | if (args.count("serial") != 0) { 65 | if (args.at("serial") != soapyInfo.at("serial")) { 66 | continue; 67 | } 68 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by serial %s", soapyInfo.at("serial").c_str()); 69 | } 70 | 71 | results.push_back(soapyInfo); 72 | } 73 | return results; 74 | } 75 | 76 | static SoapySDR::Device *makeAirspyHF(const SoapySDR::Kwargs &args) 77 | { 78 | return new SoapyAirspyHF(args); 79 | } 80 | 81 | static SoapySDR::Registry registerAirspyHF("airspyhf", &findAirspyHF, &makeAirspyHF, SOAPY_SDR_ABI_VERSION); 82 | -------------------------------------------------------------------------------- /Settings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Charles J. Cliffe 5 | * Copyright (c) 2018 Corey Stotts 6 | 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 | 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | 27 | #include "SoapyAirspyHF.hpp" 28 | 29 | SoapyAirspyHF::SoapyAirspyHF(const SoapySDR::Kwargs &args) 30 | { 31 | sampleRate = 768000; 32 | centerFrequency = 0; 33 | 34 | numBuffers = DEFAULT_NUM_BUFFERS; 35 | 36 | agcMode = 1; 37 | rfBias = false; 38 | bitPack = false; 39 | lnaGain=0; 40 | rfGain=4; 41 | hasgains=false; 42 | 43 | bufferedElems = 0; 44 | resetBuffer = false; 45 | 46 | streamActive = false; 47 | sampleRateChanged.store(false); 48 | 49 | dev = nullptr; 50 | std::stringstream serialstr; 51 | serialstr.str(""); 52 | 53 | if (args.count("serial") != 0) 54 | { 55 | try { 56 | serial = std::stoull(args.at("serial"), nullptr, 16); 57 | } catch (const std::invalid_argument &) { 58 | throw std::runtime_error("serial is not a hex number"); 59 | } catch (const std::out_of_range &) { 60 | throw std::runtime_error("serial value of out range"); 61 | } 62 | serialstr << std::hex << serial; 63 | if (airspyhf_open_sn(&dev, serial) != AIRSPYHF_SUCCESS) { 64 | throw std::runtime_error("Unable to open AirspyHF device with S/N " + serialstr.str()); 65 | } 66 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Found AirspyHF+ device: serial = %16Lx", serial); 67 | } 68 | else 69 | { 70 | if (airspyhf_open(&dev) != AIRSPYHF_SUCCESS) { 71 | throw std::runtime_error("Unable to open AirspyHF device"); 72 | } 73 | } 74 | 75 | #ifdef HASGAINS 76 | if (airspyhf_set_hf_att(dev,rfGain)==AIRSPYHF_SUCCESS) { 77 | hasgains=true; 78 | airspyhf_set_hf_lna(dev,lnaGain); 79 | airspyhf_set_hf_agc(dev,agcMode); 80 | } 81 | #endif 82 | 83 | //apply arguments to settings when they match 84 | for (const auto &info : this->getSettingInfo()) 85 | { 86 | const auto it = args.find(info.key); 87 | if (it != args.end()) this->writeSetting(it->first, it->second); 88 | } 89 | } 90 | 91 | SoapyAirspyHF::~SoapyAirspyHF(void) 92 | { 93 | std::lock_guard lock(_general_state_mutex); 94 | airspyhf_close(dev); 95 | } 96 | 97 | /******************************************************************* 98 | * Identification API 99 | ******************************************************************/ 100 | 101 | std::string SoapyAirspyHF::getDriverKey(void) const 102 | { 103 | return "AirspyHF"; 104 | } 105 | 106 | std::string SoapyAirspyHF::getHardwareKey(void) const 107 | { 108 | return "AirspyHF"; 109 | } 110 | 111 | SoapySDR::Kwargs SoapyAirspyHF::getHardwareInfo(void) const 112 | { 113 | //key/value pairs for any useful information 114 | //this also gets printed in --probe 115 | SoapySDR::Kwargs args; 116 | 117 | std::stringstream serialstr; 118 | serialstr.str(""); 119 | serialstr << std::hex << serial; 120 | args["serial"] = serialstr.str(); 121 | 122 | return args; 123 | } 124 | 125 | /******************************************************************* 126 | * Channels API 127 | ******************************************************************/ 128 | 129 | size_t SoapyAirspyHF::getNumChannels(const int dir) const 130 | { 131 | return (dir == SOAPY_SDR_RX) ? 1 : 0; 132 | } 133 | 134 | /******************************************************************* 135 | * Antenna API 136 | ******************************************************************/ 137 | 138 | std::vector SoapyAirspyHF::listAntennas(const int direction, const size_t channel) const 139 | { 140 | std::vector antennas; 141 | antennas.push_back("RX"); 142 | return antennas; 143 | } 144 | 145 | void SoapyAirspyHF::setAntenna(const int direction, const size_t channel, const std::string &name) 146 | { 147 | // TODO 148 | } 149 | 150 | std::string SoapyAirspyHF::getAntenna(const int direction, const size_t channel) const 151 | { 152 | // eventually could change this to HF/VHF 153 | return "RX"; 154 | } 155 | 156 | /******************************************************************* 157 | * Frontend corrections API 158 | ******************************************************************/ 159 | 160 | bool SoapyAirspyHF::hasDCOffsetMode(const int direction, const size_t channel) const 161 | { 162 | return false; 163 | } 164 | 165 | /******************************************************************* 166 | * Gain API 167 | ******************************************************************/ 168 | 169 | std::vector SoapyAirspyHF::listGains(const int direction, const size_t channel) const 170 | { 171 | //list available gain elements, 172 | //the functions below have a "name" parameter 173 | std::vector results; 174 | 175 | if (hasgains) { 176 | results.push_back("LNA"); 177 | results.push_back("RF"); 178 | } 179 | 180 | return results; 181 | } 182 | 183 | bool SoapyAirspyHF::hasGainMode(const int direction, const size_t channel) const 184 | { 185 | return true; // we have agc on/off setting or it's forced on, either way AGC is supported 186 | } 187 | 188 | void SoapyAirspyHF::setGainMode(const int direction, const size_t channel, const bool automatic) 189 | { 190 | //SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting AGC: %s", automatic ? "Automatic" : "Manual"); 191 | if (!hasgains) return; 192 | 193 | #ifdef HASGAINS 194 | std::lock_guard lock(_general_state_mutex); 195 | airspyhf_set_hf_agc(dev,agcMode=automatic ? 1:0); 196 | #endif 197 | } 198 | 199 | bool SoapyAirspyHF::getGainMode(const int direction, const size_t channel) const 200 | { 201 | return agcMode ? true : false; //agc is finally not always on 202 | } 203 | 204 | SoapySDR::Range SoapyAirspyHF::getGainRange(const int direction, const size_t channel, const std::string &name) const 205 | { 206 | if (!hasgains) return SoapySDR::Range(0.0, 0.0); 207 | if (name == "LNA") return SoapySDR::Range(0,6,6); 208 | return SoapySDR::Range(-48.0,0,6); 209 | } 210 | 211 | double SoapyAirspyHF::getGain(const int direction, const size_t channel, const std::string &name) const 212 | { 213 | if (!hasgains) return 0.0; 214 | if (name=="LNA") return lnaGain*6.0; 215 | return -(int)rfGain*6.0; 216 | } 217 | 218 | void SoapyAirspyHF::setGain(const int direction, const size_t channel, const std::string &name, const double value) 219 | { 220 | if (!hasgains) return; 221 | #ifdef HASGAINS 222 | std::lock_guard lock(_general_state_mutex); 223 | if (name == "LNA") { 224 | lnaGain = value>=3.0 ? 1 : 0; 225 | airspyhf_set_hf_lna(dev,lnaGain); 226 | return; 227 | } 228 | double newval = -value; 229 | if (newval<0.0) newval=0.0; 230 | if (newval>48.0) newval=48.0; 231 | rfGain=(uint8_t)(newval/6.0+0.499); 232 | airspyhf_set_hf_att(dev,rfGain); 233 | #endif 234 | } 235 | 236 | /******************************************************************* 237 | * Frequency API 238 | ******************************************************************/ 239 | 240 | void SoapyAirspyHF::setFrequency( 241 | const int direction, 242 | const size_t channel, 243 | const std::string &name, 244 | const double frequency, 245 | const SoapySDR::Kwargs &args) 246 | { 247 | if (name == "RF") 248 | { 249 | centerFrequency = (uint32_t) frequency; 250 | std::lock_guard lock(_general_state_mutex); 251 | resetBuffer = true; 252 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting center freq: %d", centerFrequency); 253 | airspyhf_set_freq(dev, centerFrequency); 254 | } 255 | } 256 | 257 | double SoapyAirspyHF::getFrequency(const int direction, const size_t channel, const std::string &name) const 258 | { 259 | if (name == "RF") 260 | { 261 | return (double) centerFrequency; 262 | } 263 | 264 | return 0; 265 | } 266 | 267 | std::vector SoapyAirspyHF::listFrequencies(const int direction, const size_t channel) const 268 | { 269 | std::vector names; 270 | names.push_back("RF"); 271 | return names; 272 | } 273 | 274 | SoapySDR::RangeList SoapyAirspyHF::getFrequencyRange( 275 | const int direction, 276 | const size_t channel, 277 | const std::string &name) const 278 | { 279 | SoapySDR::RangeList results; 280 | if (name == "RF") 281 | { 282 | results.push_back(SoapySDR::Range(9000,31000000)); 283 | results.push_back(SoapySDR::Range(60000000,260000000)); 284 | } 285 | return results; 286 | } 287 | 288 | SoapySDR::ArgInfoList SoapyAirspyHF::getFrequencyArgsInfo(const int direction, const size_t channel) const 289 | { 290 | SoapySDR::ArgInfoList freqArgs; 291 | 292 | // TODO: frequency arguments 293 | 294 | return freqArgs; 295 | } 296 | 297 | /******************************************************************* 298 | * Sample Rate API 299 | ******************************************************************/ 300 | 301 | void SoapyAirspyHF::setSampleRate(const int direction, const size_t channel, const double rate) 302 | { 303 | SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting sample rate: %d", sampleRate); 304 | 305 | if (sampleRate != rate) { 306 | sampleRate = rate; 307 | resetBuffer = true; 308 | sampleRateChanged.store(true); 309 | } 310 | } 311 | 312 | double SoapyAirspyHF::getSampleRate(const int direction, const size_t channel) const 313 | { 314 | return sampleRate; 315 | } 316 | 317 | std::vector SoapyAirspyHF::listSampleRates(const int direction, const size_t channel) const 318 | { 319 | std::vector results; 320 | 321 | std::lock_guard lock(_general_state_mutex); 322 | 323 | uint32_t numRates; 324 | airspyhf_get_samplerates(dev, &numRates, 0); 325 | 326 | std::vector samplerates; 327 | samplerates.resize(numRates); 328 | 329 | airspyhf_get_samplerates(dev, samplerates.data(), numRates); 330 | 331 | for (auto i: samplerates) { 332 | results.push_back(i); 333 | } 334 | 335 | return results; 336 | } 337 | 338 | void SoapyAirspyHF::setBandwidth(const int direction, const size_t channel, const double bw) 339 | { 340 | SoapySDR::Device::setBandwidth(direction, channel, bw); 341 | } 342 | 343 | double SoapyAirspyHF::getBandwidth(const int direction, const size_t channel) const 344 | { 345 | return SoapySDR::Device::getBandwidth(direction, channel); 346 | } 347 | 348 | std::vector SoapyAirspyHF::listBandwidths(const int direction, const size_t channel) const 349 | { 350 | std::vector results; 351 | 352 | return results; 353 | } 354 | 355 | /******************************************************************* 356 | * Settings API 357 | ******************************************************************/ 358 | 359 | SoapySDR::ArgInfoList SoapyAirspyHF::getSettingInfo(void) const 360 | { 361 | SoapySDR::ArgInfoList setArgs; 362 | 363 | return setArgs; 364 | } 365 | 366 | void SoapyAirspyHF::writeSetting(const std::string &key, const std::string &value) 367 | { 368 | 369 | } 370 | 371 | std::string SoapyAirspyHF::readSetting(const std::string &key) const 372 | { 373 | SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); 374 | return ""; 375 | } 376 | -------------------------------------------------------------------------------- /SoapyAirspyHF.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Charles J. Cliffe 5 | * Copyright (c) 2018 Corey Stotts 6 | 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 | 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | #pragma once 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include 42 | 43 | #define DEFAULT_BUFFER_BYTES 262144 44 | #define DEFAULT_NUM_BUFFERS 8 45 | #define MAX_DEVICES 32 46 | 47 | class SoapyAirspyHF: public SoapySDR::Device 48 | { 49 | public: 50 | SoapyAirspyHF(const SoapySDR::Kwargs &args); 51 | 52 | ~SoapyAirspyHF(void); 53 | 54 | /******************************************************************* 55 | * Identification API 56 | ******************************************************************/ 57 | 58 | std::string getDriverKey(void) const; 59 | 60 | std::string getHardwareKey(void) const; 61 | 62 | SoapySDR::Kwargs getHardwareInfo(void) const; 63 | 64 | /******************************************************************* 65 | * Channels API 66 | ******************************************************************/ 67 | 68 | size_t getNumChannels(const int) const; 69 | 70 | /******************************************************************* 71 | * Stream API 72 | ******************************************************************/ 73 | 74 | std::vector getStreamFormats(const int direction, const size_t channel) const; 75 | 76 | std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; 77 | 78 | SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; 79 | 80 | SoapySDR::Stream *setupStream(const int direction, const std::string &format, const std::vector &channels = 81 | std::vector(), const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); 82 | 83 | void closeStream(SoapySDR::Stream *stream); 84 | 85 | size_t getStreamMTU(SoapySDR::Stream *stream) const; 86 | 87 | int activateStream( 88 | SoapySDR::Stream *stream, 89 | const int flags = 0, 90 | const long long timeNs = 0, 91 | const size_t numElems = 0); 92 | 93 | int deactivateStream(SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0); 94 | 95 | int readStream( 96 | SoapySDR::Stream *stream, 97 | void * const *buffs, 98 | const size_t numElems, 99 | int &flags, 100 | long long &timeNs, 101 | const long timeoutUs = 100000); 102 | 103 | /******************************************************************* 104 | * Direct buffer access API 105 | ******************************************************************/ 106 | 107 | size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); 108 | 109 | int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); 110 | 111 | int acquireReadBuffer( 112 | SoapySDR::Stream *stream, 113 | size_t &handle, 114 | const void **buffs, 115 | int &flags, 116 | long long &timeNs, 117 | const long timeoutUs = 100000); 118 | 119 | void releaseReadBuffer( 120 | SoapySDR::Stream *stream, 121 | const size_t handle); 122 | 123 | /******************************************************************* 124 | * Antenna API 125 | ******************************************************************/ 126 | 127 | std::vector listAntennas(const int direction, const size_t channel) const; 128 | 129 | void setAntenna(const int direction, const size_t channel, const std::string &name); 130 | 131 | std::string getAntenna(const int direction, const size_t channel) const; 132 | 133 | /******************************************************************* 134 | * Frontend corrections API 135 | ******************************************************************/ 136 | 137 | bool hasDCOffsetMode(const int direction, const size_t channel) const; 138 | 139 | /******************************************************************* 140 | * Gain API 141 | ******************************************************************/ 142 | 143 | std::vector listGains(const int direction, const size_t channel) const; 144 | 145 | bool hasGainMode(const int direction, const size_t channel) const; 146 | 147 | void setGainMode(const int direction, const size_t channel, const bool automatic); 148 | 149 | bool getGainMode(const int direction, const size_t channel) const; 150 | 151 | // void setGain(const int direction, const size_t channel, const double value); 152 | 153 | void setGain(const int direction, const size_t channel, const std::string &name, const double value); 154 | 155 | double getGain(const int direction, const size_t channel, const std::string &name) const; 156 | 157 | SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const; 158 | 159 | /******************************************************************* 160 | * Frequency API 161 | ******************************************************************/ 162 | 163 | void setFrequency( 164 | const int direction, 165 | const size_t channel, 166 | const std::string &name, 167 | const double frequency, 168 | const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); 169 | 170 | double getFrequency(const int direction, const size_t channel, const std::string &name) const; 171 | 172 | std::vector listFrequencies(const int direction, const size_t channel) const; 173 | 174 | SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const; 175 | 176 | SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; 177 | 178 | /******************************************************************* 179 | * Sample Rate API 180 | ******************************************************************/ 181 | 182 | void setSampleRate(const int direction, const size_t channel, const double rate); 183 | 184 | double getSampleRate(const int direction, const size_t channel) const; 185 | 186 | std::vector listSampleRates(const int direction, const size_t channel) const; 187 | 188 | void setBandwidth(const int direction, const size_t channel, const double bw); 189 | 190 | double getBandwidth(const int direction, const size_t channel) const; 191 | 192 | std::vector listBandwidths(const int direction, const size_t channel) const; 193 | 194 | /******************************************************************* 195 | * Utility 196 | ******************************************************************/ 197 | 198 | /******************************************************************* 199 | * Settings API 200 | ******************************************************************/ 201 | 202 | SoapySDR::ArgInfoList getSettingInfo(void) const; 203 | 204 | void writeSetting(const std::string &key, const std::string &value); 205 | 206 | std::string readSetting(const std::string &key) const; 207 | 208 | private: 209 | 210 | //device handle 211 | uint64_t serial; 212 | airspyhf_device_t *dev; 213 | 214 | //cached settings 215 | bool hasgains; 216 | uint32_t sampleRate, centerFrequency; 217 | unsigned int bufferLength; 218 | size_t numBuffers; 219 | bool streamActive, rfBias, bitPack; 220 | uint8_t lnaGain,rfGain, agcMode; 221 | std::atomic_bool sampleRateChanged; 222 | int bytesPerSample; 223 | //uint8_t lnaGain, mixerGain, vgaGain; 224 | SoapySDR::ConverterRegistry::ConverterFunction converterFunction; 225 | 226 | public: 227 | //async api usage 228 | int rx_callback(airspyhf_transfer_t *t); 229 | 230 | mutable std::mutex _general_state_mutex; 231 | 232 | std::mutex _buf_mutex; 233 | std::condition_variable _buf_cond; 234 | 235 | std::vector > _buffs; 236 | size_t _buf_head; 237 | size_t _buf_tail; 238 | std::atomic _buf_count; 239 | char *_currentBuff; 240 | std::atomic _overflowEvent; 241 | size_t bufferedElems; 242 | size_t _currentHandle; 243 | bool resetBuffer; 244 | }; 245 | -------------------------------------------------------------------------------- /Streaming.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * The MIT License (MIT) 3 | * 4 | * Copyright (c) 2015 Charles J. Cliffe 5 | * Copyright (c) 2018 Corey Stotts 6 | 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 | 14 | * The above copyright notice and this permission notice shall be included in 15 | * all copies or substantial portions of the Software. 16 | 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | * THE SOFTWARE. 24 | */ 25 | 26 | #include "SoapyAirspyHF.hpp" 27 | #include 28 | #include 29 | #include 30 | #include //min 31 | #include //SHRT_MAX 32 | #include // memcpy 33 | 34 | #define SOAPY_NATIVE_FORMAT SOAPY_SDR_CF32 35 | 36 | std::vector SoapyAirspyHF::getStreamFormats(const int direction, const size_t channel) const { 37 | std::vector formats; 38 | 39 | for (const auto &target : SoapySDR::ConverterRegistry::listTargetFormats(SOAPY_NATIVE_FORMAT)) 40 | { 41 | formats.push_back(target); 42 | } 43 | 44 | return formats; 45 | } 46 | 47 | std::string SoapyAirspyHF::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { 48 | fullScale = 1.0; 49 | return SOAPY_NATIVE_FORMAT; 50 | } 51 | 52 | SoapySDR::ArgInfoList SoapyAirspyHF::getStreamArgsInfo(const int direction, const size_t channel) const { 53 | SoapySDR::ArgInfoList streamArgs; 54 | 55 | // SoapySDR::ArgInfo chanArg; 56 | // chanArg.key = "chan"; 57 | // chanArg.value = "mono_l"; 58 | // chanArg.name = "Channel Setup"; 59 | // chanArg.description = "Input channel configuration."; 60 | // chanArg.type = SoapySDR::ArgInfo::STRING; 61 | // std::vector chanOpts; 62 | // std::vector chanOptNames; 63 | // chanOpts.push_back("mono_l"); 64 | // chanOptNames.push_back("Mono Left"); 65 | // chanOpts.push_back("mono_r"); 66 | // chanOptNames.push_back("Mono Right"); 67 | // chanOpts.push_back("stereo_iq"); 68 | // chanOptNames.push_back("Complex L/R = I/Q"); 69 | // chanOpts.push_back("stereo_qi"); 70 | // chanOptNames.push_back("Complex L/R = Q/I"); 71 | // chanArg.options = chanOpts; 72 | // chanArg.optionNames = chanOptNames; 73 | // streamArgs.push_back(chanArg); 74 | 75 | return streamArgs; 76 | } 77 | 78 | /******************************************************************* 79 | * Async thread work 80 | ******************************************************************/ 81 | 82 | static int _rx_callback(airspyhf_transfer_t *t) 83 | { 84 | //printf("_rx_callback\n"); 85 | SoapyAirspyHF *self = (SoapyAirspyHF *)t->ctx; 86 | return self->rx_callback(t); 87 | } 88 | 89 | int SoapyAirspyHF::rx_callback(airspyhf_transfer_t *t) 90 | { 91 | if (sampleRateChanged.load()) { 92 | return 1; 93 | } 94 | 95 | //printf("_rx_callback %d _buf_head=%d, numBuffers=%d\n", len, _buf_head, _buf_tail); 96 | //overflow condition: the caller is not reading fast enough 97 | if (_buf_count == numBuffers) 98 | { 99 | _overflowEvent = true; 100 | return 0; 101 | } 102 | 103 | //copy into the buffer queue 104 | auto &buff = _buffs[_buf_tail]; 105 | buff.resize(t->sample_count * bytesPerSample); 106 | std::memcpy(buff.data(), t->samples, t->sample_count * bytesPerSample); 107 | 108 | //increment the tail pointer 109 | _buf_tail = (_buf_tail + 1) % numBuffers; 110 | 111 | //increment buffers available under lock 112 | //to avoid race in acquireReadBuffer wait 113 | { 114 | std::lock_guard lock(_buf_mutex); 115 | _buf_count++; 116 | } 117 | 118 | //notify readStream() 119 | _buf_cond.notify_one(); 120 | 121 | return 0; 122 | } 123 | 124 | /******************************************************************* 125 | * Stream API 126 | ******************************************************************/ 127 | 128 | SoapySDR::Stream *SoapyAirspyHF::setupStream( 129 | const int direction, 130 | const std::string &format, 131 | const std::vector &channels, 132 | const SoapySDR::Kwargs &args) 133 | { 134 | //check the channel configuration 135 | if (channels.size() > 1 or (channels.size() > 0 and channels.at(0) != 0)) { 136 | throw std::runtime_error("setupStream invalid channel selection"); 137 | } 138 | 139 | std::vector sources = SoapySDR::ConverterRegistry::listSourceFormats(format); 140 | 141 | if (std::find(sources.begin(), sources.end(), SOAPY_NATIVE_FORMAT) == sources.end()) { 142 | throw std::runtime_error( 143 | "setupStream invalid format '" + format + "'."); 144 | } 145 | 146 | converterFunction = SoapySDR::ConverterRegistry::getFunction(SOAPY_NATIVE_FORMAT, format, SoapySDR::ConverterRegistry::GENERIC); 147 | 148 | sampleRateChanged.store(true); 149 | 150 | bytesPerSample = SoapySDR::formatToSize(SOAPY_NATIVE_FORMAT); 151 | 152 | //We get this many complex samples over the bus. 153 | //Its the same for both complex float and int16. 154 | //TODO adjust when packing is enabled 155 | bufferLength = DEFAULT_BUFFER_BYTES/4; 156 | 157 | //clear async fifo counts 158 | _buf_tail = 0; 159 | _buf_count = 0; 160 | _buf_head = 0; 161 | 162 | //allocate buffers 163 | _buffs.resize(numBuffers); 164 | for (auto &buff : _buffs) buff.reserve(bufferLength*bytesPerSample); 165 | for (auto &buff : _buffs) buff.resize(bufferLength*bytesPerSample); 166 | 167 | return (SoapySDR::Stream *) this; 168 | } 169 | 170 | void SoapyAirspyHF::closeStream(SoapySDR::Stream *stream) 171 | { 172 | _buffs.clear(); 173 | } 174 | 175 | size_t SoapyAirspyHF::getStreamMTU(SoapySDR::Stream *stream) const 176 | { 177 | return bufferLength; 178 | } 179 | 180 | int SoapyAirspyHF::activateStream( 181 | SoapySDR::Stream *stream, 182 | const int flags, 183 | const long long timeNs, 184 | const size_t numElems) 185 | { 186 | if (flags != 0) { 187 | return SOAPY_SDR_NOT_SUPPORTED; 188 | } 189 | 190 | resetBuffer = true; 191 | bufferedElems = 0; 192 | 193 | std::lock_guard lock(_general_state_mutex); 194 | 195 | if (sampleRateChanged.load()) { 196 | airspyhf_set_samplerate(dev, sampleRate); 197 | sampleRateChanged.store(false); 198 | } 199 | airspyhf_start(dev, &_rx_callback, (void *) this); 200 | 201 | return 0; 202 | } 203 | 204 | int SoapyAirspyHF::deactivateStream(SoapySDR::Stream *stream, const int flags, const long long timeNs) 205 | { 206 | if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; 207 | 208 | std::lock_guard lock(_general_state_mutex); 209 | 210 | airspyhf_stop(dev); 211 | 212 | streamActive = false; 213 | 214 | return 0; 215 | } 216 | 217 | int SoapyAirspyHF::readStream( 218 | SoapySDR::Stream *stream, 219 | void * const *buffs, 220 | const size_t numElems, 221 | int &flags, 222 | long long &timeNs, 223 | const long timeoutUs) 224 | { 225 | { 226 | std::lock_guard lock(_general_state_mutex); 227 | 228 | if (sampleRateChanged.load()) { 229 | airspyhf_stop(dev); 230 | airspyhf_set_samplerate(dev, sampleRate); 231 | airspyhf_start(dev, &_rx_callback, (void *) this); 232 | sampleRateChanged.store(false); 233 | } 234 | } 235 | 236 | //this is the user's buffer for channel 0 237 | void *buff0 = buffs[0]; 238 | 239 | //are elements left in the buffer? if not, do a new read. 240 | if (bufferedElems == 0) 241 | { 242 | int ret = this->acquireReadBuffer(stream, _currentHandle, (const void **)&_currentBuff, flags, timeNs, timeoutUs); 243 | if (ret < 0) return ret; 244 | bufferedElems = ret; 245 | } 246 | 247 | size_t returnedElems = std::min(bufferedElems, numElems); 248 | 249 | //convert into user's buff0 250 | converterFunction(_currentBuff, buff0, returnedElems, 1); 251 | 252 | //bump variables for next call into readStream 253 | bufferedElems -= returnedElems; 254 | _currentBuff += returnedElems * bytesPerSample; 255 | 256 | //return number of elements written to buff0 257 | if (bufferedElems != 0) flags |= SOAPY_SDR_MORE_FRAGMENTS; 258 | else this->releaseReadBuffer(stream, _currentHandle); 259 | return returnedElems; 260 | } 261 | 262 | /******************************************************************* 263 | * Direct buffer access API 264 | ******************************************************************/ 265 | 266 | size_t SoapyAirspyHF::getNumDirectAccessBuffers(SoapySDR::Stream *stream) 267 | { 268 | return _buffs.size(); 269 | } 270 | 271 | int SoapyAirspyHF::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) 272 | { 273 | buffs[0] = (void *)_buffs[handle].data(); 274 | return 0; 275 | } 276 | 277 | int SoapyAirspyHF::acquireReadBuffer( 278 | SoapySDR::Stream *stream, 279 | size_t &handle, 280 | const void **buffs, 281 | int &flags, 282 | long long &timeNs, 283 | const long timeoutUs) 284 | { 285 | //reset is issued by various settings 286 | //to drain old data out of the queue 287 | if (resetBuffer) 288 | { 289 | //drain all buffers from the fifo 290 | _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; 291 | resetBuffer = false; 292 | _overflowEvent = false; 293 | } 294 | 295 | //handle overflow from the rx callback thread 296 | if (_overflowEvent) 297 | { 298 | //drain the old buffers from the fifo 299 | _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; 300 | _overflowEvent = false; 301 | SoapySDR::log(SOAPY_SDR_SSI, "O"); 302 | return SOAPY_SDR_OVERFLOW; 303 | } 304 | 305 | //wait for a buffer to become available 306 | if (_buf_count == 0) 307 | { 308 | std::unique_lock lock(_buf_mutex); 309 | _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs), [this]{return _buf_count != 0;}); 310 | if (_buf_count == 0) return SOAPY_SDR_TIMEOUT; 311 | } 312 | 313 | //extract handle and buffer 314 | handle = _buf_head; 315 | _buf_head = (_buf_head + 1) % numBuffers; 316 | buffs[0] = (void *)_buffs[handle].data(); 317 | flags = 0; 318 | 319 | //return number available 320 | return _buffs[handle].size() / bytesPerSample; 321 | } 322 | 323 | void SoapyAirspyHF::releaseReadBuffer( 324 | SoapySDR::Stream *stream, 325 | const size_t handle) 326 | { 327 | //TODO this wont handle out of order releases 328 | _buf_count--; 329 | } 330 | -------------------------------------------------------------------------------- /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 | soapyairspyhf (0.2.0-1) unstable; urgency=low 2 | 3 | * Release 0.2.0 (2021-06-02) 4 | 5 | -- Josh Blum Wed, 02 Jun 2021 08:55:01 -0000 6 | 7 | soapyairspyhf (0.1.0-1) unstable; urgency=low 8 | 9 | * Release 0.1.0 (2018-01-12) 10 | 11 | -- Josh Blum Sun, 30 Dec 2018 11:30:55 -0600 12 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: soapyairspyhf 2 | Section: libs 3 | Priority: optional 4 | Maintainer: Corey Stotts 5 | Uploader: Josh Blum 6 | Build-Depends: 7 | debhelper (>= 9.0.0), 8 | cmake, 9 | libairspyhf-dev, 10 | libsoapysdr-dev 11 | Standards-Version: 4.5.0 12 | Homepage: https://github.com/pothosware/SoapyAirspyHF/wiki 13 | Vcs-Git: https://github.com/pothosware/SoapyAirspyHF.git 14 | Vcs-Browser: https://github.com/pothosware/SoapyAirspyHF 15 | 16 | Package: soapysdr0.7-module-airspyhf 17 | Architecture: any 18 | Multi-Arch: same 19 | Depends: ${shlibs:Depends}, ${misc:Depends} 20 | Description: Soapy AirspyHF - AirspyHF device support for Soapy SDR. 21 | A Soapy module that supports AirspyHF devices within the Soapy API. 22 | 23 | Package: soapysdr-module-airspyhf 24 | Architecture: all 25 | Depends: soapysdr0.7-module-airspyhf, ${misc:Depends} 26 | Description: Soapy AirspyHF - AirspyHF device support for Soapy SDR. 27 | A Soapy module that supports AirspyHF devices within the Soapy API. 28 | . 29 | This is an empty dependency package that pulls in the AirspyHF 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: soapyairspyhf 3 | Source: https://github.com/pothosware/SoapyAirspyHF/wiki 4 | 5 | Files: * 6 | Copyright: 7 | Copyright (c) 2018 Corey Stotts 8 | Copyright (c) 2015 Charles J. Cliffe 9 | License: MIT 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | . 17 | The above copyright notice and this permission notice shall be included in 18 | all copies or substantial portions of the Software. 19 | . 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | THE SOFTWARE. 27 | -------------------------------------------------------------------------------- /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-airspyhf.install: -------------------------------------------------------------------------------- 1 | usr/lib/* 2 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | --------------------------------------------------------------------------------