pax_global_header00006660000000000000000000000064140557066720014526gustar00rootroot0000000000000052 comment=d6825331a6bfc3578dc6fe5091fce3d550b6af3c SoapyAirspyHF-soapy-airspyhf-0.2.0/000077500000000000000000000000001405570667200172025ustar00rootroot00000000000000SoapyAirspyHF-soapy-airspyhf-0.2.0/.gitignore000066400000000000000000000000071405570667200211670ustar00rootroot00000000000000build/ SoapyAirspyHF-soapy-airspyhf-0.2.0/CMakeLists.txt000066400000000000000000000040761405570667200217510ustar00rootroot00000000000000################################################### # Build Soapy SDR support module for Airspy Devices ################################################### cmake_minimum_required(VERSION 2.8.7) project(SoapyAirspyHF CXX) find_package(SoapySDR "0.4.0" NO_MODULE REQUIRED) if (NOT SoapySDR_FOUND) message(FATAL_ERROR "Soapy SDR development files not found...") endif () list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) find_package(LibAIRSPYHF) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LIBAIRSPYHF_INCLUDE_DIRS}) #enable c++11 features if(CMAKE_COMPILER_IS_GNUCXX) #C++11 is a required language feature for this project include(CheckCXXCompilerFlag) CHECK_CXX_COMPILER_FLAG("-std=c++11" HAS_STD_CXX11) if(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") else(HAS_STD_CXX11) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") endif() #Thread support enabled (not the same as -lpthread) list(APPEND AIRSPYHF_LIBS -pthread) #disable warnings for unused parameters add_definitions(-Wno-unused-parameter) endif(CMAKE_COMPILER_IS_GNUCXX) if (APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wc++11-extensions") endif(APPLE) # IF (APPLE) # ADD_DEFINITIONS( # -D__MACOSX_CORE__ # ) # FIND_LIBRARY(COREFOUNDATION_LIBRARY CoreFoundation) #SET (AIRSPYHF_LIBS ${COREFOUNDATION_LIBRARY} ${AIRSPYHF_LIBS} ) # ENDIF (APPLE) list(APPEND AIRSPYHF_LIBS ${LIBAIRSPYHF_LIBRARIES}) SOAPY_SDR_MODULE_UTIL( TARGET airspyhfSupport SOURCES SoapyAirspyHF.hpp Registration.cpp Settings.cpp Streaming.cpp LIBRARIES ${AIRSPYHF_LIBS} ) ######################################################################## # uninstall target ######################################################################## add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") configure_file( "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) SoapyAirspyHF-soapy-airspyhf-0.2.0/Changelog.txt000066400000000000000000000007561405570667200216420ustar00rootroot00000000000000Release 0.2.0 (2021-06-02) ========================== - Fix return early when readStream() not active - Support opening the device by serial convention - Use converter registry for sample format support - Fix return code handling for airspyhf_list_devices() - Manual gain settings in firmware R1.3 and airspyhf 1.1.2 or later Release 0.1.0 (2018-01-12) ========================== - Initial release of Soapy AirspyHF+ support module, based on the Soapy support module for the original Airspy SoapyAirspyHF-soapy-airspyhf-0.2.0/FindLibAIRSPYHF.cmake000066400000000000000000000012141405570667200226570ustar00rootroot00000000000000INCLUDE(FindPkgConfig) PKG_CHECK_MODULES(PC_LIBAIRSPYHF libairspyhf) FIND_PATH( LIBAIRSPYHF_INCLUDE_DIRS NAMES libairspyhf/airspyhf.h HINTS $ENV{LIBAIRSPY_DIR}/include ${PC_LIBAIRSPYHF_INCLUDEDIR} PATHS /usr/local/include /usr/include ) FIND_LIBRARY( LIBAIRSPYHF_LIBRARIES NAMES airspyhf HINTS $ENV{LIBAIRSPYHF_DIR}/lib ${PC_LIBAIRSPYHF_LIBDIR} PATHS /usr/local/lib /usr/lib ) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBAIRSPYHF DEFAULT_MSG LIBAIRSPYHF_LIBRARIES LIBAIRSPYHF_INCLUDE_DIRS) MARK_AS_ADVANCED(LIBAIRSPYHF_LIBRARIES LIBAIRSPYHF_INCLUDE_DIRS) SoapyAirspyHF-soapy-airspyhf-0.2.0/LICENSE.txt000066400000000000000000000021341405570667200210250ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Charles J. Cliffe Copyright (c) 2018 Corey Stotts Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SoapyAirspyHF-soapy-airspyhf-0.2.0/LibFindMacros.cmake000066400000000000000000000100421405570667200226550ustar00rootroot00000000000000# Works the same as find_package, but forwards the "REQUIRED" and "QUIET" arguments # used for the current package. For this to work, the first parameter must be the # prefix of the current package, then the prefix of the new package etc, which are # passed to find_package. macro (libfind_package PREFIX) set (LIBFIND_PACKAGE_ARGS ${ARGN}) if (${PREFIX}_FIND_QUIETLY) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} QUIET) endif (${PREFIX}_FIND_QUIETLY) if (${PREFIX}_FIND_REQUIRED) set (LIBFIND_PACKAGE_ARGS ${LIBFIND_PACKAGE_ARGS} REQUIRED) endif (${PREFIX}_FIND_REQUIRED) find_package(${LIBFIND_PACKAGE_ARGS}) endmacro (libfind_package) # CMake developers made the UsePkgConfig system deprecated in the same release (2.6) # where they added pkg_check_modules. Consequently I need to support both in my scripts # to avoid those deprecated warnings. Here's a helper that does just that. # Works identically to pkg_check_modules, except that no checks are needed prior to use. macro (libfind_pkg_check_modules PREFIX PKGNAME) if (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) include(UsePkgConfig) pkgconfig(${PKGNAME} ${PREFIX}_INCLUDE_DIRS ${PREFIX}_LIBRARY_DIRS ${PREFIX}_LDFLAGS ${PREFIX}_CFLAGS) else (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) find_package(PkgConfig) if (PKG_CONFIG_FOUND) pkg_check_modules(${PREFIX} ${PKGNAME}) endif (PKG_CONFIG_FOUND) endif (${CMAKE_MAJOR_VERSION} EQUAL 2 AND ${CMAKE_MINOR_VERSION} EQUAL 4) endmacro (libfind_pkg_check_modules) # Do the final processing once the paths have been detected. # If include dirs are needed, ${PREFIX}_PROCESS_INCLUDES should be set to contain # all the variables, each of which contain one include directory. # Ditto for ${PREFIX}_PROCESS_LIBS and library files. # Will set ${PREFIX}_FOUND, ${PREFIX}_INCLUDE_DIRS and ${PREFIX}_LIBRARIES. # Also handles errors in case library detection was required, etc. macro (libfind_process PREFIX) # Skip processing if already processed during this run if (NOT ${PREFIX}_FOUND) # Start with the assumption that the library was found set (${PREFIX}_FOUND TRUE) # Process all includes and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_INCLUDES}) if (${i}) set (${PREFIX}_INCLUDE_DIRS ${${PREFIX}_INCLUDE_DIRS} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Process all libraries and set _FOUND to false if any are missing foreach (i ${${PREFIX}_PROCESS_LIBS}) if (${i}) set (${PREFIX}_LIBRARIES ${${PREFIX}_LIBRARIES} ${${i}}) mark_as_advanced(${i}) else (${i}) set (${PREFIX}_FOUND FALSE) endif (${i}) endforeach (i) # Print message and/or exit on fatal error if (${PREFIX}_FOUND) if (NOT ${PREFIX}_FIND_QUIETLY) message (STATUS "Found ${PREFIX} ${${PREFIX}_VERSION}") endif (NOT ${PREFIX}_FIND_QUIETLY) else (${PREFIX}_FOUND) if (${PREFIX}_FIND_REQUIRED) foreach (i ${${PREFIX}_PROCESS_INCLUDES} ${${PREFIX}_PROCESS_LIBS}) message("${i}=${${i}}") endforeach (i) 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.") endif (${PREFIX}_FIND_REQUIRED) endif (${PREFIX}_FOUND) endif (NOT ${PREFIX}_FOUND) endmacro (libfind_process) macro(libfind_library PREFIX basename) set(TMP "") if(MSVC80) set(TMP -vc80) endif(MSVC80) if(MSVC90) set(TMP -vc90) endif(MSVC90) set(${PREFIX}_LIBNAMES ${basename}${TMP}) if(${ARGC} GREATER 2) set(${PREFIX}_LIBNAMES ${basename}${TMP}-${ARGV2}) string(REGEX REPLACE "\\." "_" TMP ${${PREFIX}_LIBNAMES}) set(${PREFIX}_LIBNAMES ${${PREFIX}_LIBNAMES} ${TMP}) endif(${ARGC} GREATER 2) find_library(${PREFIX}_LIBRARY NAMES ${${PREFIX}_LIBNAMES} PATHS ${${PREFIX}_PKGCONF_LIBRARY_DIRS} ) endmacro(libfind_library) SoapyAirspyHF-soapy-airspyhf-0.2.0/README.md000066400000000000000000000005171405570667200204640ustar00rootroot00000000000000# Soapy SDR plugin for AirspyHF+ ## Dependencies * SoapySDR - https://github.com/pothosware/SoapySDR/wiki * libairspyhf - https://github.com/airspy/airspyhf ## Documentation * https://github.com/pothosware/SoapyAirspyHF/wiki Turning AGC on/off, LNA and RF gain settings requires firmware R1.3 or later and airspyhf 1.1.2 or later.SoapyAirspyHF-soapy-airspyhf-0.2.0/Registration.cpp000066400000000000000000000056151405570667200223670ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Copyright (c) 2018 Corey Stotts * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyAirspyHF.hpp" #include #include //malloc #include static std::vector findAirspyHF(const SoapySDR::Kwargs &args) { std::vector results; airspyhf_lib_version_t asVersion; airspyhf_lib_version(&asVersion); // SoapySDR_setLogLevel(SOAPY_SDR_DEBUG); SoapySDR_logf(SOAPY_SDR_DEBUG, "AirSpyHF Lib v%d.%d rev %d", asVersion.major_version, asVersion.minor_version, asVersion.revision); uint64_t serials[MAX_DEVICES]; int count = airspyhf_list_devices(serials, MAX_DEVICES); if (count == AIRSPYHF_ERROR) { SoapySDR_logf(SOAPY_SDR_ERROR, "libairspyhf error listing devices"); return results; } SoapySDR_logf(SOAPY_SDR_DEBUG, "%d AirSpy boards found.", count); for (int i = 0; i < count; i++) { std::stringstream serialstr; serialstr.str(""); serialstr << std::hex << serials[i]; SoapySDR_logf(SOAPY_SDR_DEBUG, "Serial %s", serialstr.str().c_str()); SoapySDR::Kwargs soapyInfo; soapyInfo["label"] = "AirSpy HF+ [" + serialstr.str() + "]"; soapyInfo["serial"] = serialstr.str(); if (args.count("serial") != 0) { if (args.at("serial") != soapyInfo.at("serial")) { continue; } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by serial %s", soapyInfo.at("serial").c_str()); } results.push_back(soapyInfo); } return results; } static SoapySDR::Device *makeAirspyHF(const SoapySDR::Kwargs &args) { return new SoapyAirspyHF(args); } static SoapySDR::Registry registerAirspyHF("airspyhf", &findAirspyHF, &makeAirspyHF, SOAPY_SDR_ABI_VERSION); SoapyAirspyHF-soapy-airspyhf-0.2.0/Settings.cpp000066400000000000000000000252151405570667200215130ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Copyright (c) 2018 Corey Stotts * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyAirspyHF.hpp" #if AIRSPYHF_VER_MAJOR >= 1 #if AIRSPYHF_VER_MINOR >= 1 #if AIRSPYHF_VER_REVISION >= 2 #define HASGAINS #endif #endif #endif SoapyAirspyHF::SoapyAirspyHF(const SoapySDR::Kwargs &args) { sampleRate = 768000; centerFrequency = 0; numBuffers = DEFAULT_NUM_BUFFERS; agcMode = 1; rfBias = false; bitPack = false; lnaGain=0; rfGain=4; hasgains=false; bufferedElems = 0; resetBuffer = false; streamActive = false; sampleRateChanged.store(false); dev = nullptr; std::stringstream serialstr; serialstr.str(""); if (args.count("serial") != 0) { try { serial = std::stoull(args.at("serial"), nullptr, 16); } catch (const std::invalid_argument &) { throw std::runtime_error("serial is not a hex number"); } catch (const std::out_of_range &) { throw std::runtime_error("serial value of out range"); } serialstr << std::hex << serial; if (airspyhf_open_sn(&dev, serial) != AIRSPYHF_SUCCESS) { throw std::runtime_error("Unable to open AirspyHF device with S/N " + serialstr.str()); } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found AirspyHF+ device: serial = %16Lx", serial); } else { if (airspyhf_open(&dev) != AIRSPYHF_SUCCESS) { throw std::runtime_error("Unable to open AirspyHF device"); } } #ifdef HASGAINS if (airspyhf_set_hf_att(dev,rfGain)==AIRSPYHF_SUCCESS) { hasgains=true; airspyhf_set_hf_lna(dev,lnaGain); airspyhf_set_hf_agc(dev,agcMode); } #endif //apply arguments to settings when they match for (const auto &info : this->getSettingInfo()) { const auto it = args.find(info.key); if (it != args.end()) this->writeSetting(it->first, it->second); } } SoapyAirspyHF::~SoapyAirspyHF(void) { std::lock_guard lock(_general_state_mutex); airspyhf_close(dev); } /******************************************************************* * Identification API ******************************************************************/ std::string SoapyAirspyHF::getDriverKey(void) const { return "AirspyHF"; } std::string SoapyAirspyHF::getHardwareKey(void) const { return "AirspyHF"; } SoapySDR::Kwargs SoapyAirspyHF::getHardwareInfo(void) const { //key/value pairs for any useful information //this also gets printed in --probe SoapySDR::Kwargs args; std::stringstream serialstr; serialstr.str(""); serialstr << std::hex << serial; args["serial"] = serialstr.str(); return args; } /******************************************************************* * Channels API ******************************************************************/ size_t SoapyAirspyHF::getNumChannels(const int dir) const { return (dir == SOAPY_SDR_RX) ? 1 : 0; } /******************************************************************* * Antenna API ******************************************************************/ std::vector SoapyAirspyHF::listAntennas(const int direction, const size_t channel) const { std::vector antennas; antennas.push_back("RX"); return antennas; } void SoapyAirspyHF::setAntenna(const int direction, const size_t channel, const std::string &name) { // TODO } std::string SoapyAirspyHF::getAntenna(const int direction, const size_t channel) const { // eventually could change this to HF/VHF return "RX"; } /******************************************************************* * Frontend corrections API ******************************************************************/ bool SoapyAirspyHF::hasDCOffsetMode(const int direction, const size_t channel) const { return false; } /******************************************************************* * Gain API ******************************************************************/ std::vector SoapyAirspyHF::listGains(const int direction, const size_t channel) const { //list available gain elements, //the functions below have a "name" parameter std::vector results; if (hasgains) { results.push_back("LNA"); results.push_back("RF"); } return results; } bool SoapyAirspyHF::hasGainMode(const int direction, const size_t channel) const { return true; // we have agc on/off setting or it's forced on, either way AGC is supported } void SoapyAirspyHF::setGainMode(const int direction, const size_t channel, const bool automatic) { //SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting AGC: %s", automatic ? "Automatic" : "Manual"); if (!hasgains) return; #ifdef HASGAINS std::lock_guard lock(_general_state_mutex); airspyhf_set_hf_agc(dev,agcMode=automatic ? 1:0); #endif } bool SoapyAirspyHF::getGainMode(const int direction, const size_t channel) const { return agcMode ? true : false; //agc is finally not always on } SoapySDR::Range SoapyAirspyHF::getGainRange(const int direction, const size_t channel, const std::string &name) const { if (!hasgains) return SoapySDR::Range(0.0, 0.0); if (name == "LNA") return SoapySDR::Range(0,6,6); return SoapySDR::Range(-48.0,0,6); } double SoapyAirspyHF::getGain(const int direction, const size_t channel, const std::string &name) const { if (!hasgains) return 0.0; if (name=="LNA") return lnaGain*6.0; return -(int)rfGain*6.0; } void SoapyAirspyHF::setGain(const int direction, const size_t channel, const std::string &name, const double value) { if (!hasgains) return; #ifdef HASGAINS std::lock_guard lock(_general_state_mutex); if (name == "LNA") { lnaGain = value>=3.0 ? 1 : 0; airspyhf_set_hf_lna(dev,lnaGain); return; } double newval = -value; if (newval<0.0) newval=0.0; if (newval>48.0) newval=48.0; rfGain=(uint8_t)(newval/6.0+0.499); airspyhf_set_hf_att(dev,rfGain); #endif } /******************************************************************* * Frequency API ******************************************************************/ void SoapyAirspyHF::setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args) { if (name == "RF") { centerFrequency = (uint32_t) frequency; std::lock_guard lock(_general_state_mutex); resetBuffer = true; SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting center freq: %d", centerFrequency); airspyhf_set_freq(dev, centerFrequency); } } double SoapyAirspyHF::getFrequency(const int direction, const size_t channel, const std::string &name) const { if (name == "RF") { return (double) centerFrequency; } return 0; } std::vector SoapyAirspyHF::listFrequencies(const int direction, const size_t channel) const { std::vector names; names.push_back("RF"); return names; } SoapySDR::RangeList SoapyAirspyHF::getFrequencyRange( const int direction, const size_t channel, const std::string &name) const { SoapySDR::RangeList results; if (name == "RF") { results.push_back(SoapySDR::Range(9000,31000000)); results.push_back(SoapySDR::Range(60000000,260000000)); } return results; } SoapySDR::ArgInfoList SoapyAirspyHF::getFrequencyArgsInfo(const int direction, const size_t channel) const { SoapySDR::ArgInfoList freqArgs; // TODO: frequency arguments return freqArgs; } /******************************************************************* * Sample Rate API ******************************************************************/ void SoapyAirspyHF::setSampleRate(const int direction, const size_t channel, const double rate) { SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting sample rate: %d", sampleRate); if (sampleRate != rate) { sampleRate = rate; resetBuffer = true; sampleRateChanged.store(true); } } double SoapyAirspyHF::getSampleRate(const int direction, const size_t channel) const { return sampleRate; } std::vector SoapyAirspyHF::listSampleRates(const int direction, const size_t channel) const { std::vector results; std::lock_guard lock(_general_state_mutex); uint32_t numRates; airspyhf_get_samplerates(dev, &numRates, 0); std::vector samplerates; samplerates.resize(numRates); airspyhf_get_samplerates(dev, samplerates.data(), numRates); for (auto i: samplerates) { results.push_back(i); } return results; } void SoapyAirspyHF::setBandwidth(const int direction, const size_t channel, const double bw) { SoapySDR::Device::setBandwidth(direction, channel, bw); } double SoapyAirspyHF::getBandwidth(const int direction, const size_t channel) const { return SoapySDR::Device::getBandwidth(direction, channel); } std::vector SoapyAirspyHF::listBandwidths(const int direction, const size_t channel) const { std::vector results; return results; } /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList SoapyAirspyHF::getSettingInfo(void) const { SoapySDR::ArgInfoList setArgs; return setArgs; } void SoapyAirspyHF::writeSetting(const std::string &key, const std::string &value) { } std::string SoapyAirspyHF::readSetting(const std::string &key) const { SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); return ""; } SoapyAirspyHF-soapy-airspyhf-0.2.0/SoapyAirspyHF.hpp000066400000000000000000000207701405570667200224220ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Copyright (c) 2018 Corey Stotts * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEFAULT_BUFFER_BYTES 262144 #define DEFAULT_NUM_BUFFERS 8 #define MAX_DEVICES 32 class SoapyAirspyHF: public SoapySDR::Device { public: SoapyAirspyHF(const SoapySDR::Kwargs &args); ~SoapyAirspyHF(void); /******************************************************************* * Identification API ******************************************************************/ std::string getDriverKey(void) const; std::string getHardwareKey(void) const; SoapySDR::Kwargs getHardwareInfo(void) const; /******************************************************************* * Channels API ******************************************************************/ size_t getNumChannels(const int) const; /******************************************************************* * Stream API ******************************************************************/ std::vector getStreamFormats(const int direction, const size_t channel) const; std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; SoapySDR::Stream *setupStream(const int direction, const std::string &format, const std::vector &channels = std::vector(), const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); void closeStream(SoapySDR::Stream *stream); size_t getStreamMTU(SoapySDR::Stream *stream) const; int activateStream( SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0, const size_t numElems = 0); int deactivateStream(SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0); int readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs = 100000); /******************************************************************* * Direct buffer access API ******************************************************************/ size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); int acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs = 100000); void releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle); /******************************************************************* * Antenna API ******************************************************************/ std::vector listAntennas(const int direction, const size_t channel) const; void setAntenna(const int direction, const size_t channel, const std::string &name); std::string getAntenna(const int direction, const size_t channel) const; /******************************************************************* * Frontend corrections API ******************************************************************/ bool hasDCOffsetMode(const int direction, const size_t channel) const; /******************************************************************* * Gain API ******************************************************************/ std::vector listGains(const int direction, const size_t channel) const; bool hasGainMode(const int direction, const size_t channel) const; void setGainMode(const int direction, const size_t channel, const bool automatic); bool getGainMode(const int direction, const size_t channel) const; // void setGain(const int direction, const size_t channel, const double value); void setGain(const int direction, const size_t channel, const std::string &name, const double value); double getGain(const int direction, const size_t channel, const std::string &name) const; SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const; /******************************************************************* * Frequency API ******************************************************************/ void setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); double getFrequency(const int direction, const size_t channel, const std::string &name) const; std::vector listFrequencies(const int direction, const size_t channel) const; SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const; SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; /******************************************************************* * Sample Rate API ******************************************************************/ void setSampleRate(const int direction, const size_t channel, const double rate); double getSampleRate(const int direction, const size_t channel) const; std::vector listSampleRates(const int direction, const size_t channel) const; void setBandwidth(const int direction, const size_t channel, const double bw); double getBandwidth(const int direction, const size_t channel) const; std::vector listBandwidths(const int direction, const size_t channel) const; /******************************************************************* * Utility ******************************************************************/ /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList getSettingInfo(void) const; void writeSetting(const std::string &key, const std::string &value); std::string readSetting(const std::string &key) const; private: //device handle uint64_t serial; airspyhf_device_t *dev; //cached settings bool hasgains; uint32_t sampleRate, centerFrequency; unsigned int bufferLength; size_t numBuffers; bool streamActive, rfBias, bitPack; uint8_t lnaGain,rfGain, agcMode; std::atomic_bool sampleRateChanged; int bytesPerSample; //uint8_t lnaGain, mixerGain, vgaGain; SoapySDR::ConverterRegistry::ConverterFunction converterFunction; public: //async api usage int rx_callback(airspyhf_transfer_t *t); mutable std::mutex _general_state_mutex; std::mutex _buf_mutex; std::condition_variable _buf_cond; std::vector > _buffs; size_t _buf_head; size_t _buf_tail; std::atomic _buf_count; char *_currentBuff; std::atomic _overflowEvent; size_t bufferedElems; size_t _currentHandle; bool resetBuffer; }; SoapyAirspyHF-soapy-airspyhf-0.2.0/Streaming.cpp000066400000000000000000000235541405570667200216500ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * Copyright (c) 2018 Corey Stotts * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "SoapyAirspyHF.hpp" #include #include #include #include //min #include //SHRT_MAX #include // memcpy #define SOAPY_NATIVE_FORMAT SOAPY_SDR_CF32 std::vector SoapyAirspyHF::getStreamFormats(const int direction, const size_t channel) const { std::vector formats; for (const auto &target : SoapySDR::ConverterRegistry::listTargetFormats(SOAPY_NATIVE_FORMAT)) { formats.push_back(target); } return formats; } std::string SoapyAirspyHF::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { fullScale = 1.0; return SOAPY_NATIVE_FORMAT; } SoapySDR::ArgInfoList SoapyAirspyHF::getStreamArgsInfo(const int direction, const size_t channel) const { SoapySDR::ArgInfoList streamArgs; // SoapySDR::ArgInfo chanArg; // chanArg.key = "chan"; // chanArg.value = "mono_l"; // chanArg.name = "Channel Setup"; // chanArg.description = "Input channel configuration."; // chanArg.type = SoapySDR::ArgInfo::STRING; // std::vector chanOpts; // std::vector chanOptNames; // chanOpts.push_back("mono_l"); // chanOptNames.push_back("Mono Left"); // chanOpts.push_back("mono_r"); // chanOptNames.push_back("Mono Right"); // chanOpts.push_back("stereo_iq"); // chanOptNames.push_back("Complex L/R = I/Q"); // chanOpts.push_back("stereo_qi"); // chanOptNames.push_back("Complex L/R = Q/I"); // chanArg.options = chanOpts; // chanArg.optionNames = chanOptNames; // streamArgs.push_back(chanArg); return streamArgs; } /******************************************************************* * Async thread work ******************************************************************/ static int _rx_callback(airspyhf_transfer_t *t) { //printf("_rx_callback\n"); SoapyAirspyHF *self = (SoapyAirspyHF *)t->ctx; return self->rx_callback(t); } int SoapyAirspyHF::rx_callback(airspyhf_transfer_t *t) { if (sampleRateChanged.load()) { return 1; } //printf("_rx_callback %d _buf_head=%d, numBuffers=%d\n", len, _buf_head, _buf_tail); //overflow condition: the caller is not reading fast enough if (_buf_count == numBuffers) { _overflowEvent = true; return 0; } //copy into the buffer queue auto &buff = _buffs[_buf_tail]; buff.resize(t->sample_count * bytesPerSample); std::memcpy(buff.data(), t->samples, t->sample_count * bytesPerSample); //increment the tail pointer _buf_tail = (_buf_tail + 1) % numBuffers; //increment buffers available under lock //to avoid race in acquireReadBuffer wait { std::lock_guard lock(_buf_mutex); _buf_count++; } //notify readStream() _buf_cond.notify_one(); return 0; } /******************************************************************* * Stream API ******************************************************************/ SoapySDR::Stream *SoapyAirspyHF::setupStream( const int direction, const std::string &format, const std::vector &channels, const SoapySDR::Kwargs &args) { //check the channel configuration if (channels.size() > 1 or (channels.size() > 0 and channels.at(0) != 0)) { throw std::runtime_error("setupStream invalid channel selection"); } std::vector sources = SoapySDR::ConverterRegistry::listSourceFormats(format); if (std::find(sources.begin(), sources.end(), SOAPY_NATIVE_FORMAT) == sources.end()) { throw std::runtime_error( "setupStream invalid format '" + format + "'."); } converterFunction = SoapySDR::ConverterRegistry::getFunction(SOAPY_NATIVE_FORMAT, format, SoapySDR::ConverterRegistry::GENERIC); sampleRateChanged.store(true); bytesPerSample = SoapySDR::formatToSize(SOAPY_NATIVE_FORMAT); //We get this many complex samples over the bus. //Its the same for both complex float and int16. //TODO adjust when packing is enabled bufferLength = DEFAULT_BUFFER_BYTES/4; //clear async fifo counts _buf_tail = 0; _buf_count = 0; _buf_head = 0; //allocate buffers _buffs.resize(numBuffers); for (auto &buff : _buffs) buff.reserve(bufferLength*bytesPerSample); for (auto &buff : _buffs) buff.resize(bufferLength*bytesPerSample); return (SoapySDR::Stream *) this; } void SoapyAirspyHF::closeStream(SoapySDR::Stream *stream) { _buffs.clear(); } size_t SoapyAirspyHF::getStreamMTU(SoapySDR::Stream *stream) const { return bufferLength; } int SoapyAirspyHF::activateStream( SoapySDR::Stream *stream, const int flags, const long long timeNs, const size_t numElems) { if (flags != 0) { return SOAPY_SDR_NOT_SUPPORTED; } resetBuffer = true; bufferedElems = 0; std::lock_guard lock(_general_state_mutex); if (sampleRateChanged.load()) { airspyhf_set_samplerate(dev, sampleRate); sampleRateChanged.store(false); } airspyhf_start(dev, &_rx_callback, (void *) this); return 0; } int SoapyAirspyHF::deactivateStream(SoapySDR::Stream *stream, const int flags, const long long timeNs) { if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; std::lock_guard lock(_general_state_mutex); airspyhf_stop(dev); streamActive = false; return 0; } int SoapyAirspyHF::readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs) { { std::lock_guard lock(_general_state_mutex); if (sampleRateChanged.load()) { airspyhf_stop(dev); airspyhf_set_samplerate(dev, sampleRate); airspyhf_start(dev, &_rx_callback, (void *) this); sampleRateChanged.store(false); } } //this is the user's buffer for channel 0 void *buff0 = buffs[0]; //are elements left in the buffer? if not, do a new read. if (bufferedElems == 0) { int ret = this->acquireReadBuffer(stream, _currentHandle, (const void **)&_currentBuff, flags, timeNs, timeoutUs); if (ret < 0) return ret; bufferedElems = ret; } size_t returnedElems = std::min(bufferedElems, numElems); //convert into user's buff0 converterFunction(_currentBuff, buff0, returnedElems, 1); //bump variables for next call into readStream bufferedElems -= returnedElems; _currentBuff += returnedElems * bytesPerSample; //return number of elements written to buff0 if (bufferedElems != 0) flags |= SOAPY_SDR_MORE_FRAGMENTS; else this->releaseReadBuffer(stream, _currentHandle); return returnedElems; } /******************************************************************* * Direct buffer access API ******************************************************************/ size_t SoapyAirspyHF::getNumDirectAccessBuffers(SoapySDR::Stream *stream) { return _buffs.size(); } int SoapyAirspyHF::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) { buffs[0] = (void *)_buffs[handle].data(); return 0; } int SoapyAirspyHF::acquireReadBuffer( SoapySDR::Stream *stream, size_t &handle, const void **buffs, int &flags, long long &timeNs, const long timeoutUs) { //reset is issued by various settings //to drain old data out of the queue if (resetBuffer) { //drain all buffers from the fifo _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; resetBuffer = false; _overflowEvent = false; } //handle overflow from the rx callback thread if (_overflowEvent) { //drain the old buffers from the fifo _buf_head = (_buf_head + _buf_count.exchange(0)) % numBuffers; _overflowEvent = false; SoapySDR::log(SOAPY_SDR_SSI, "O"); return SOAPY_SDR_OVERFLOW; } //wait for a buffer to become available if (_buf_count == 0) { std::unique_lock lock(_buf_mutex); _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs), [this]{return _buf_count != 0;}); if (_buf_count == 0) return SOAPY_SDR_TIMEOUT; } //extract handle and buffer handle = _buf_head; _buf_head = (_buf_head + 1) % numBuffers; buffs[0] = (void *)_buffs[handle].data(); flags = 0; //return number available return _buffs[handle].size() / bytesPerSample; } void SoapyAirspyHF::releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle) { //TODO this wont handle out of order releases _buf_count--; } SoapyAirspyHF-soapy-airspyhf-0.2.0/cmake/000077500000000000000000000000001405570667200202625ustar00rootroot00000000000000SoapyAirspyHF-soapy-airspyhf-0.2.0/cmake/cmake_uninstall.cmake.in000066400000000000000000000020131405570667200250360ustar00rootroot00000000000000if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") message(FATAL_ERROR "Cannot find install manifest: @CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) string(REGEX REPLACE "\n" ";" files "${files}") foreach(file ${files}) message(STATUS "Uninstalling $ENV{DESTDIR}${file}") if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") exec_program( "@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" OUTPUT_VARIABLE rm_out RETURN_VALUE rm_retval ) if(NOT "${rm_retval}" STREQUAL 0) message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") endif(NOT "${rm_retval}" STREQUAL 0) else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") message(STATUS "File $ENV{DESTDIR}${file} does not exist.") endif(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") endforeach(file) SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/000077500000000000000000000000001405570667200204245ustar00rootroot00000000000000SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/changelog000066400000000000000000000004511405570667200222760ustar00rootroot00000000000000soapyairspyhf (0.2.0-1) unstable; urgency=low * Release 0.2.0 (2021-06-02) -- Josh Blum Wed, 02 Jun 2021 08:55:01 -0000 soapyairspyhf (0.1.0-1) unstable; urgency=low * Release 0.1.0 (2018-01-12) -- Josh Blum Sun, 30 Dec 2018 11:30:55 -0600 SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/compat000066400000000000000000000000021405570667200216220ustar00rootroot000000000000009 SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/control000066400000000000000000000020351405570667200220270ustar00rootroot00000000000000Source: soapyairspyhf Section: libs Priority: optional Maintainer: Corey Stotts Uploader: Josh Blum Build-Depends: debhelper (>= 9.0.0), cmake, libairspyhf-dev, libsoapysdr-dev Standards-Version: 4.5.0 Homepage: https://github.com/pothosware/SoapyAirspyHF/wiki Vcs-Git: https://github.com/pothosware/SoapyAirspyHF.git Vcs-Browser: https://github.com/pothosware/SoapyAirspyHF Package: soapysdr0.7-module-airspyhf Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: Soapy AirspyHF - AirspyHF device support for Soapy SDR. A Soapy module that supports AirspyHF devices within the Soapy API. Package: soapysdr-module-airspyhf Architecture: all Depends: soapysdr0.7-module-airspyhf, ${misc:Depends} Description: Soapy AirspyHF - AirspyHF device support for Soapy SDR. A Soapy module that supports AirspyHF devices within the Soapy API. . This is an empty dependency package that pulls in the AirspyHF module for the default version of libsoapysdr. SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/copyright000066400000000000000000000024411405570667200223600ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: soapyairspyhf Source: https://github.com/pothosware/SoapyAirspyHF/wiki Files: * Copyright: Copyright (c) 2018 Corey Stotts Copyright (c) 2015 Charles J. Cliffe License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/docs000066400000000000000000000000121405570667200212700ustar00rootroot00000000000000README.md SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/rules000077500000000000000000000006161405570667200215070ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) export DEB_HOST_MULTIARCH # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 %: dh $@ --buildsystem=cmake --parallel override_dh_auto_configure: dh_auto_configure -- -DLIB_SUFFIX="/$(DEB_HOST_MULTIARCH)" override_dh_installchangelogs: dh_installchangelogs Changelog.txt SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/soapysdr0.7-module-airspyhf.install000066400000000000000000000000121405570667200272040ustar00rootroot00000000000000usr/lib/* SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/source/000077500000000000000000000000001405570667200217245ustar00rootroot00000000000000SoapyAirspyHF-soapy-airspyhf-0.2.0/debian/source/format000066400000000000000000000000141405570667200231320ustar00rootroot000000000000003.0 (quilt)