pax_global_header00006660000000000000000000000064131012066250014506gustar00rootroot0000000000000052 comment=d04a0bd41aed119519993de8c273784cc36d9041 SoapyAirspy-soapy-airspy-0.1.1/000077500000000000000000000000001310120662500164265ustar00rootroot00000000000000SoapyAirspy-soapy-airspy-0.1.1/.gitignore000066400000000000000000000000201310120662500204060ustar00rootroot00000000000000build/ .vscode/ SoapyAirspy-soapy-airspy-0.1.1/CMakeLists.txt000066400000000000000000000031761310120662500211750ustar00rootroot00000000000000################################################### # Build Soapy SDR support module for Airspy Devices ################################################### cmake_minimum_required(VERSION 2.8.7) project(SoapyAirspy 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(LibAIRSPY) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${LIBAIRSPY_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 AIRSPY_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 (AIRSPY_LIBS ${COREFOUNDATION_LIBRARY} ${AIRSPY_LIBS} ) # ENDIF (APPLE) list(APPEND AIRSPY_LIBS ${LIBAIRSPY_LIBRARIES}) SOAPY_SDR_MODULE_UTIL( TARGET airspySupport SOURCES SoapyAirspy.hpp Registation.cpp Settings.cpp Streaming.cpp LIBRARIES ${AIRSPY_LIBS} ) SoapyAirspy-soapy-airspy-0.1.1/Changelog.txt000066400000000000000000000004531310120662500210600ustar00rootroot00000000000000Release 0.1.1 (2017-04-29) ========================== - Support native AIRSPY_SAMPLE_INT16_IQ format - Use atomics for ring buffer implementation - Use Format string constants for stream types Release 0.1.0 (2016-09-01) ========================== - Initial release of Soapy Airspy support module SoapyAirspy-soapy-airspy-0.1.1/FindLibAIRSPY.cmake000066400000000000000000000011641310120662500216710ustar00rootroot00000000000000INCLUDE(FindPkgConfig) PKG_CHECK_MODULES(PC_LIBAIRSPY libairspy) FIND_PATH( LIBAIRSPY_INCLUDE_DIRS NAMES libairspy/airspy.h HINTS $ENV{LIBAIRSPY_DIR}/include ${PC_LIBAIRSPY_INCLUDEDIR} PATHS /usr/local/include /usr/include ) FIND_LIBRARY( LIBAIRSPY_LIBRARIES NAMES airspy HINTS $ENV{LIBAIRSPY_DIR}/lib ${PC_LIBAIRSPY_LIBDIR} PATHS /usr/local/lib /usr/lib ) INCLUDE(FindPackageHandleStandardArgs) FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBAIRSPY DEFAULT_MSG LIBAIRSPY_LIBRARIES LIBAIRSPY_INCLUDE_DIRS) MARK_AS_ADVANCED(LIBAIRSPY_LIBRARIES LIBAIRSPY_INCLUDE_DIRS) SoapyAirspy-soapy-airspy-0.1.1/LICENSE.txt000066400000000000000000000020741310120662500202540ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2015 Charles J. Cliffe 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. SoapyAirspy-soapy-airspy-0.1.1/LibFindMacros.cmake000066400000000000000000000100421310120662500221010ustar00rootroot00000000000000# 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) SoapyAirspy-soapy-airspy-0.1.1/README.md000066400000000000000000000003351310120662500177060ustar00rootroot00000000000000# Soapy SDR plugin for Airspy ##Dependencies * SoapySDR - https://github.com/pothosware/SoapySDR/wiki * libairspy - https://github.com/airspy/host/wiki ##Documentation * https://github.com/pothosware/SoapyAirspy/wiki SoapyAirspy-soapy-airspy-0.1.1/Registation.cpp000066400000000000000000000122011310120662500214160ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * 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 "SoapyAirspy.hpp" #include #include //malloc #include static std::vector findAirspy(const SoapySDR::Kwargs &args) { std::vector results; airspy_lib_version_t asVersion; airspy_lib_version(&asVersion); // SoapySDR_setLogLevel(SOAPY_SDR_DEBUG); SoapySDR_logf(SOAPY_SDR_DEBUG, "AirSpy Lib v%d.%d rev %d", asVersion.major_version, asVersion.minor_version, asVersion.revision); int numDevices = 0; std::vector< struct airspy_device * > foundDevices; int status = 0; // if (args.count("serial") != 0) { // std::stringstream serialstr; // // uint32_t serialNum[2]; // std::string serial_in(args.at("serial")); // std::replace( serial_in.begin(), serial_in.end(), ':', ' '); // serialstr.str(serial_in); // serialstr << std::hex; // serialstr >> serialNum[0]; // serialstr >> serialNum[1]; // // uint64_t serial64 = ((uint64_t) serialNum[0] << 32) | serialNum[1]); // // SoapySDR_logf(SOAPY_SDR_DEBUG, "Serial? '%s' %u %u 64: %llu", serialstr.str().c_str(), serialNum[0], serialNum[1], serial64); // // struct airspy_device *searchDev = nullptr; // status = airspy_open_sn(&searchDev, serial64); // // SoapySDR_logf(SOAPY_SDR_DEBUG, "Search done.."); // // if (status == AIRSPY_SUCCESS) { // foundDevices.push_back(searchDev); // } else { // SoapySDR_logf(SOAPY_SDR_DEBUG, "Error finding by serial.."); // } // } else { for (int i = 0, iMax = MAX_DEVICES; i < iMax; i++) { struct airspy_device *searchDev = nullptr; status = airspy_open(&searchDev); if (status != AIRSPY_SUCCESS) { break; } foundDevices.push_back(searchDev); } } SoapySDR_logf(SOAPY_SDR_DEBUG, "%d AirSpy boards found.", foundDevices.size()); int devId = 0; for (std::vector< struct airspy_device * >::iterator i = foundDevices.begin(); i != foundDevices.end(); i++) { uint8_t id = AIRSPY_BOARD_ID_INVALID; airspy_read_partid_serialno_t serial; status = airspy_board_id_read(*i, &id); if (status != AIRSPY_SUCCESS) { continue; } status = airspy_board_partid_serialno_read(*i, &serial); if (status != AIRSPY_SUCCESS) { continue; } std::string boardName(airspy_board_id_name((enum airspy_board_id)id)); std::stringstream serialstr; serialstr.str(""); serialstr << std::hex << serial.serial_no[2] << ":" << serial.serial_no[3]; SoapySDR_logf(SOAPY_SDR_DEBUG, "Serial %s", serialstr.str().c_str()); SoapySDR::Kwargs soapyInfo; soapyInfo["device_id"] = std::to_string(devId); soapyInfo["label"] = boardName + " [" + serialstr.str() + "]"; soapyInfo["serial"] = serialstr.str(); devId++; // 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()); // } else if (args.count("device_id") != 0) { if (args.at("device_id") != soapyInfo.at("device_id")) { continue; } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by device_id %s", soapyInfo.at("device_id").c_str()); } results.push_back(soapyInfo); } for (std::vector< struct airspy_device * >::iterator i = foundDevices.begin(); i != foundDevices.end(); i++) { airspy_close(*i); } return results; } static SoapySDR::Device *makeAirspy(const SoapySDR::Kwargs &args) { return new SoapyAirspy(args); } static SoapySDR::Registry registerAirspy("airspy", &findAirspy, &makeAirspy, SOAPY_SDR_ABI_VERSION); SoapyAirspy-soapy-airspy-0.1.1/Settings.cpp000066400000000000000000000257701310120662500207450ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * 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 "SoapyAirspy.hpp" SoapyAirspy::SoapyAirspy(const SoapySDR::Kwargs &args) { deviceId = -1; sampleRate = 3000000; centerFrequency = 0; numBuffers = DEFAULT_NUM_BUFFERS; agcMode = false; rfBias = false; bufferedElems = 0; resetBuffer = false; streamActive = false; sampleRateChanged.store(false); dev = nullptr; lnaGain = mixerGain = vgaGain = 0; if (args.count("device_id") != 0) { try { deviceId = std::stoi(args.at("device_id")); } catch (const std::invalid_argument &) { throw std::runtime_error("device_id invalid."); } std::vector allDevs; int status; for (int i = 0, iMax = deviceId; i <= iMax; i++) { struct airspy_device *searchDev = nullptr; status = airspy_open(&searchDev); if (status != AIRSPY_SUCCESS) { continue; } allDevs.push_back(searchDev); } int numDevices = allDevs.size(); if (deviceId < 0 || deviceId >= numDevices) { for (std::vector< struct airspy_device * >::iterator i = allDevs.begin(); i != allDevs.end(); i++) { airspy_close(*i); } throw std::runtime_error("Airspy device_id out of range [0 .. " + std::to_string(numDevices) + "]."); } dev = allDevs[deviceId]; for (std::vector< struct airspy_device * >::iterator i = allDevs.begin(); i != allDevs.end(); i++) { if (*i != dev) { airspy_close(*i); } } SoapySDR_logf(SOAPY_SDR_DEBUG, "Found Airspy device using 'device_id' = %d", deviceId); } if (deviceId == -1) { throw std::runtime_error("device_id missing."); } } SoapyAirspy::~SoapyAirspy(void) { airspy_close(dev); } /******************************************************************* * Identification API ******************************************************************/ std::string SoapyAirspy::getDriverKey(void) const { return "Airspy"; } std::string SoapyAirspy::getHardwareKey(void) const { return "Airspy"; } SoapySDR::Kwargs SoapyAirspy::getHardwareInfo(void) const { //key/value pairs for any useful information //this also gets printed in --probe SoapySDR::Kwargs args; args["origin"] = "https://github.com/pothosware/SoapyAirspy"; args["device_id"] = std::to_string(deviceId); return args; } /******************************************************************* * Channels API ******************************************************************/ size_t SoapyAirspy::getNumChannels(const int dir) const { return (dir == SOAPY_SDR_RX) ? 1 : 0; } /******************************************************************* * Antenna API ******************************************************************/ std::vector SoapyAirspy::listAntennas(const int direction, const size_t channel) const { std::vector antennas; antennas.push_back("RX"); return antennas; } void SoapyAirspy::setAntenna(const int direction, const size_t channel, const std::string &name) { // TODO } std::string SoapyAirspy::getAntenna(const int direction, const size_t channel) const { return "RX"; } /******************************************************************* * Frontend corrections API ******************************************************************/ bool SoapyAirspy::hasDCOffsetMode(const int direction, const size_t channel) const { return false; } /******************************************************************* * Gain API ******************************************************************/ std::vector SoapyAirspy::listGains(const int direction, const size_t channel) const { //list available gain elements, //the functions below have a "name" parameter std::vector results; results.push_back("LNA"); results.push_back("MIX"); results.push_back("VGA"); return results; } bool SoapyAirspy::hasGainMode(const int direction, const size_t channel) const { return true; } void SoapyAirspy::setGainMode(const int direction, const size_t channel, const bool automatic) { agcMode = automatic; airspy_set_lna_agc(dev, agcMode?1:0); airspy_set_mixer_agc(dev, agcMode?1:0); SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting AGC: %s", automatic ? "Automatic" : "Manual"); } bool SoapyAirspy::getGainMode(const int direction, const size_t channel) const { return agcMode; } void SoapyAirspy::setGain(const int direction, const size_t channel, const double value) { //set the overall gain by distributing it across available gain elements //OR delete this function to use SoapySDR's default gain distribution algorithm... SoapySDR::Device::setGain(direction, channel, value); } void SoapyAirspy::setGain(const int direction, const size_t channel, const std::string &name, const double value) { if (name == "LNA") { lnaGain = uint8_t(value); airspy_set_lna_gain(dev, lnaGain); } else if (name == "MIX") { mixerGain = uint8_t(value); airspy_set_mixer_gain(dev, mixerGain); } else if (name == "VGA") { vgaGain = uint8_t(value); airspy_set_vga_gain(dev, vgaGain); } } double SoapyAirspy::getGain(const int direction, const size_t channel, const std::string &name) const { if (name == "LNA") { return lnaGain; } else if (name == "MIX") { return mixerGain; } else if (name == "VGA") { return vgaGain; } return 0; } SoapySDR::Range SoapyAirspy::getGainRange(const int direction, const size_t channel, const std::string &name) const { if (name == "LNA" || name == "MIX" || name == "VGA") { return SoapySDR::Range(0, 15); } return SoapySDR::Range(0, 15); } /******************************************************************* * Frequency API ******************************************************************/ void SoapyAirspy::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; resetBuffer = true; SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting center freq: %d", centerFrequency); airspy_set_freq(dev, centerFrequency); } } double SoapyAirspy::getFrequency(const int direction, const size_t channel, const std::string &name) const { if (name == "RF") { return (double) centerFrequency; } return 0; } std::vector SoapyAirspy::listFrequencies(const int direction, const size_t channel) const { std::vector names; names.push_back("RF"); return names; } SoapySDR::RangeList SoapyAirspy::getFrequencyRange( const int direction, const size_t channel, const std::string &name) const { SoapySDR::RangeList results; if (name == "RF") { results.push_back(SoapySDR::Range(24000000, 1800000000)); } return results; } SoapySDR::ArgInfoList SoapyAirspy::getFrequencyArgsInfo(const int direction, const size_t channel) const { SoapySDR::ArgInfoList freqArgs; // TODO: frequency arguments return freqArgs; } /******************************************************************* * Sample Rate API ******************************************************************/ void SoapyAirspy::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 SoapyAirspy::getSampleRate(const int direction, const size_t channel) const { return sampleRate; } std::vector SoapyAirspy::listSampleRates(const int direction, const size_t channel) const { std::vector results; uint32_t numRates; airspy_get_samplerates(dev, &numRates, 0); std::vector samplerates; samplerates.resize(numRates); airspy_get_samplerates(dev, samplerates.data(), numRates); for (auto i: samplerates) { results.push_back(i); } return results; } void SoapyAirspy::setBandwidth(const int direction, const size_t channel, const double bw) { SoapySDR::Device::setBandwidth(direction, channel, bw); } double SoapyAirspy::getBandwidth(const int direction, const size_t channel) const { return SoapySDR::Device::getBandwidth(direction, channel); } std::vector SoapyAirspy::listBandwidths(const int direction, const size_t channel) const { std::vector results; return results; } /******************************************************************* * Settings API ******************************************************************/ SoapySDR::ArgInfoList SoapyAirspy::getSettingInfo(void) const { SoapySDR::ArgInfoList setArgs; // Bias-T SoapySDR::ArgInfo biasOffsetArg; biasOffsetArg.key = "biastee"; biasOffsetArg.value = "false"; biasOffsetArg.name = "Bias tee"; biasOffsetArg.description = "Enable the 4.5v DC Bias tee to power SpyVerter / LNA / etc. via antenna connection."; biasOffsetArg.type = SoapySDR::ArgInfo::BOOL; setArgs.push_back(biasOffsetArg); return setArgs; } void SoapyAirspy::writeSetting(const std::string &key, const std::string &value) { if (key == "biastee") { bool enable = (value == "true"); airspy_set_rf_bias(dev, enable); } } std::string SoapyAirspy::readSetting(const std::string &key) const { if (key == "biastee") { return rfBias?"true":"false"; } // SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); return ""; } SoapyAirspy-soapy-airspy-0.1.1/SoapyAirspy.hpp000066400000000000000000000203551310120662500214270ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * 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 #define DEFAULT_BUFFER_BYTES 262144 #define DEFAULT_NUM_BUFFERS 8 #define MAX_DEVICES 32 class SoapyAirspy: public SoapySDR::Device { public: SoapyAirspy(const SoapySDR::Kwargs &args); ~SoapyAirspy(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 int deviceId; struct airspy_device *dev; //cached settings uint32_t sampleRate, centerFrequency; unsigned int bufferLength; size_t numBuffers; bool agcMode, streamActive, rfBias; std::atomic_bool sampleRateChanged; int bytesPerSample; uint8_t lnaGain, mixerGain, vgaGain; public: //async api usage int rx_callback(airspy_transfer *t); 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; }; SoapyAirspy-soapy-airspy-0.1.1/Streaming.cpp000066400000000000000000000233011310120662500210620ustar00rootroot00000000000000/* * The MIT License (MIT) * * Copyright (c) 2015 Charles J. Cliffe * 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 "SoapyAirspy.hpp" #include #include #include //min #include //SHRT_MAX #include // memcpy std::vector SoapyAirspy::getStreamFormats(const int direction, const size_t channel) const { std::vector formats; // formats.push_back("CS8"); formats.push_back(SOAPY_SDR_CS16); formats.push_back(SOAPY_SDR_CF32); return formats; } std::string SoapyAirspy::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { fullScale = 65536; return SOAPY_SDR_CS16; } SoapySDR::ArgInfoList SoapyAirspy::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(airspy_transfer *t) { //printf("_rx_callback\n"); SoapyAirspy *self = (SoapyAirspy *)t->ctx; return self->rx_callback(t); } int SoapyAirspy::rx_callback(airspy_transfer *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 *SoapyAirspy::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"); } airspy_sample_type asFormat = AIRSPY_SAMPLE_INT16_IQ; //check the format if (format == SOAPY_SDR_CF32) { SoapySDR_log(SOAPY_SDR_INFO, "Using format CF32."); asFormat = AIRSPY_SAMPLE_FLOAT32_IQ; } else if (format == SOAPY_SDR_CS16) { SoapySDR_log(SOAPY_SDR_INFO, "Using format CS16."); asFormat = AIRSPY_SAMPLE_INT16_IQ; } else { throw std::runtime_error( "setupStream invalid format '" + format + "' -- Only CS16 and CF32 are supported by SoapyAirspy module."); } airspy_set_sample_type(dev, asFormat); sampleRateChanged.store(true); bytesPerSample = SoapySDR::formatToSize(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 SoapyAirspy::closeStream(SoapySDR::Stream *stream) { _buffs.clear(); } size_t SoapyAirspy::getStreamMTU(SoapySDR::Stream *stream) const { return bufferLength; } int SoapyAirspy::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; if (sampleRateChanged.load()) { airspy_set_samplerate(dev, sampleRate); sampleRateChanged.store(false); } airspy_start_rx(dev, &_rx_callback, (void *) this); return 0; } int SoapyAirspy::deactivateStream(SoapySDR::Stream *stream, const int flags, const long long timeNs) { if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; airspy_stop_rx(dev); streamActive = false; return 0; } int SoapyAirspy::readStream( SoapySDR::Stream *stream, void * const *buffs, const size_t numElems, int &flags, long long &timeNs, const long timeoutUs) { if (!airspy_is_streaming(dev)) { return 0; } if (sampleRateChanged.load()) { airspy_stop_rx(dev); airspy_set_samplerate(dev, sampleRate); airspy_start_rx(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 std::memcpy(buff0, _currentBuff, returnedElems * bytesPerSample); //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 SoapyAirspy::getNumDirectAccessBuffers(SoapySDR::Stream *stream) { return _buffs.size(); } int SoapyAirspy::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) { buffs[0] = (void *)_buffs[handle].data(); return 0; } int SoapyAirspy::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 SoapyAirspy::releaseReadBuffer( SoapySDR::Stream *stream, const size_t handle) { //TODO this wont handle out of order releases _buf_count--; } SoapyAirspy-soapy-airspy-0.1.1/debian/000077500000000000000000000000001310120662500176505ustar00rootroot00000000000000SoapyAirspy-soapy-airspy-0.1.1/debian/changelog000066400000000000000000000004431310120662500215230ustar00rootroot00000000000000soapyairspy (0.1.1-1) unstable; urgency=low * Release 0.1.1 (2017-04-29) -- Josh Blum Sat, 29 Apr 2017 15:02:58 -0000 soapyairspy (0.1.0) unstable; urgency=low * Release 0.1.1 (2016-09-01) -- Josh Blum Thu, 01 Sep 2016 17:23:53 -0700 SoapyAirspy-soapy-airspy-0.1.1/debian/compat000066400000000000000000000000021310120662500210460ustar00rootroot000000000000009 SoapyAirspy-soapy-airspy-0.1.1/debian/control000066400000000000000000000020021310120662500212450ustar00rootroot00000000000000Source: soapyairspy Section: hamradio Priority: optional Maintainer: Charles J. Cliffe Uploaders: Josh Blum Build-Depends: debhelper (>= 9.0.0), cmake, libsoapysdr-dev, libairspy-dev Standards-Version: 3.9.8 Homepage: https://github.com/pothosware/SoapyAirspy/wiki Vcs-Git: https://github.com/pothosware/SoapyAirspy.git Vcs-Browser: https://github.com/pothosware/SoapyAirspy Package: soapysdr0.6-module-airspy Architecture: any Multi-Arch: same Depends: ${shlibs:Depends}, ${misc:Depends} Description: Airspy device support for SoapySDR The Soapy Airspy project provides a SoapySDR hardware support module. Package: soapysdr-module-airspy Architecture: all Depends: soapysdr0.6-module-airspy, ${misc:Depends} Description: Airspy device support for SoapySDR (default version) The Soapy Airspy project provides a SoapySDR hardware support module. . This is an empty dependency package that pulls in the Airspy module for the default version of libsoapysdr. SoapyAirspy-soapy-airspy-0.1.1/debian/copyright000066400000000000000000000023651310120662500216110ustar00rootroot00000000000000Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: soapyairspy Source: https://github.com/pothosware/SoapyAirspy/wiki Files: * Copyright: Copyright (c) 2016 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. SoapyAirspy-soapy-airspy-0.1.1/debian/docs000066400000000000000000000000121310120662500205140ustar00rootroot00000000000000README.md SoapyAirspy-soapy-airspy-0.1.1/debian/rules000077500000000000000000000005641310120662500207350ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_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 SoapyAirspy-soapy-airspy-0.1.1/debian/soapysdr0.6-module-airspy.install000066400000000000000000000000121310120662500261110ustar00rootroot00000000000000usr/lib/* SoapyAirspy-soapy-airspy-0.1.1/debian/source/000077500000000000000000000000001310120662500211505ustar00rootroot00000000000000SoapyAirspy-soapy-airspy-0.1.1/debian/source/format000066400000000000000000000000141310120662500223560ustar00rootroot000000000000003.0 (quilt)