diff options
-rw-r--r-- | CMakeLists.txt | 27 | ||||
-rw-r--r-- | Dummy_Registration.cpp | 47 | ||||
-rw-r--r-- | Dummy_Session.cpp | 52 | ||||
-rw-r--r-- | Dummy_Settings.cpp | 302 | ||||
-rw-r--r-- | Dummy_Streaming.cpp | 296 | ||||
-rw-r--r-- | LICENSE | 22 | ||||
-rw-r--r-- | README.md | 36 | ||||
-rw-r--r-- | SoapyDummy.hpp | 207 | ||||
-rw-r--r-- | self_test.py | 81 |
9 files changed, 1070 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6010eb7 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +######################################################################## +# CMake project for building the Soapy Dummy driver +######################################################################## + +cmake_minimum_required(VERSION 2.8.7) +project(SoapyDummy CXX) +set(CMAKE_BUILD_TYPE "Debug") + +find_package(SoapySDR "0.4.0" NO_MODULE) + if (NOT SoapySDR_FOUND) + message(FATAL_ERROR "Soapy SDR development files not found...") + endif () + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-unused-parameter") + +SOAPY_SDR_MODULE_UTIL( + TARGET DummySupport + SOURCES + Dummy_Registration.cpp + Dummy_Settings.cpp + Dummy_Streaming.cpp + Dummy_Session.cpp +) diff --git a/Dummy_Registration.cpp b/Dummy_Registration.cpp new file mode 100644 index 0000000..7e9081c --- /dev/null +++ b/Dummy_Registration.cpp @@ -0,0 +1,47 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Matthias P. Braendli + * + * 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 "SoapyDummy.hpp" +#include <SoapySDR/Registry.hpp> + +static std::vector<SoapySDR::Kwargs> find_Dummy(const SoapySDR::Kwargs &args) +{ + SoapyDummySession sess; + + std::vector<SoapySDR::Kwargs> results; + + SoapySDR::Kwargs options; + options["device"] = "dummy0"; + options["version"] = "1"; + results.push_back(options); + + return results; +} + +static SoapySDR::Device *make_Dummy(const SoapySDR::Kwargs &args) +{ + return new SoapyDummy(args); +} + +static SoapySDR::Registry register_dummy("dummy", &find_Dummy, &make_Dummy, SOAPY_SDR_ABI_VERSION); diff --git a/Dummy_Session.cpp b/Dummy_Session.cpp new file mode 100644 index 0000000..1732149 --- /dev/null +++ b/Dummy_Session.cpp @@ -0,0 +1,52 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Matthias P. Braendli + * + * 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 "SoapyDummy.hpp" +#include <SoapySDR/Logger.hpp> +#include <mutex> +#include <cstddef> + +static std::mutex sessionMutex; +static size_t sessionCount = 0; + +SoapyDummySession::SoapyDummySession(void) +{ + std::lock_guard<std::mutex> lock(sessionMutex); + + if (sessionCount == 0) { + SoapySDR::logf(SOAPY_SDR_INFO, "Opening dummy session"); + } + sessionCount++; +} + +SoapyDummySession::~SoapyDummySession(void) +{ + std::lock_guard<std::mutex> lock(sessionMutex); + + sessionCount--; + if (sessionCount == 0) + { + SoapySDR::logf(SOAPY_SDR_INFO, "Last dummy session closed"); + } +} diff --git a/Dummy_Settings.cpp b/Dummy_Settings.cpp new file mode 100644 index 0000000..acf8614 --- /dev/null +++ b/Dummy_Settings.cpp @@ -0,0 +1,302 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Matthias P. Braendli + * + * 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 "SoapyDummy.hpp" + + +SoapyDummy::SoapyDummy( const SoapySDR::Kwargs &args ) +{ +} + +SoapyDummy::~SoapyDummy( void ) +{ +} + +/******************************************************************* + * Identification API + ******************************************************************/ +std::string SoapyDummy::getDriverKey( void ) const +{ + return("Dummy"); +} + +std::string SoapyDummy::getHardwareKey( void ) const +{ + return "DummyHWKey"; +} + +SoapySDR::Kwargs SoapyDummy::getHardwareInfo( void ) const +{ + SoapySDR::Kwargs info; + info["version"] = "1"; + info["clock source"] = "fictious"; + return info; +} + +/******************************************************************* + * Channels API + ******************************************************************/ +size_t SoapyDummy::getNumChannels( const int dir ) const +{ + return 1; +} + + +bool SoapyDummy::getFullDuplex( const int direction, const size_t channel ) const +{ + return true; +} + +/******************************************************************* + * Settings API + ******************************************************************/ +SoapySDR::ArgInfoList SoapyDummy::getSettingInfo(void) const +{ + SoapySDR::ArgInfoList setArgs; + + /* example setting from hackrf + SoapySDR::ArgInfo biastxArg; + biastxArg.key="bias_tx"; + biastxArg.value="false"; + biastxArg.name="Antenna Bias"; + biastxArg.description="Antenna port power control."; + biastxArg.type=SoapySDR::ArgInfo::BOOL; + setArgs.push_back(biastxArg); + */ + + return setArgs; +} + +void SoapyDummy::writeSetting(const std::string &key, const std::string &value) +{ + /* + if(key=="bias_tx"){ + std::lock_guard<std::mutex> lock(_device_mutex); + _tx_stream.bias=(value=="true") ? true : false; + int ret=hackrf_set_antenna_enable(_dev,_tx_stream.bias); + if(ret!=HACKRF_SUCCESS){ + + SoapySDR_logf(SOAPY_SDR_INFO,"Failed to apply antenna bias voltage"); + + } + } + */ +} + +std::string SoapyDummy::readSetting(const std::string &key) const +{ + /* + if (key == "bias_tx") { + return _tx_stream.bias?"true":"false"; + } + */ + return ""; +} + +/******************************************************************* + * Antenna API + ******************************************************************/ +std::vector<std::string> SoapyDummy::listAntennas( const int direction, const size_t channel ) const +{ + std::vector<std::string> options; + if ( direction == SOAPY_SDR_RX ) { + options.push_back("RX"); + } + else { + options.push_back("TX"); + } + return options; +} + +void SoapyDummy::setAntenna( const int direction, const size_t channel, const std::string &name ) +{ + /* TODO delete this function or throw if name != RX... */ +} + +std::string SoapyDummy::getAntenna( const int direction, const size_t channel ) const +{ + if ( direction == SOAPY_SDR_RX ) { + return "RX"; + } + else { + return "TX"; + } +} + +/******************************************************************* + * Frontend corrections API + ******************************************************************/ +bool SoapyDummy::hasDCOffsetMode( const int direction, const size_t channel ) const +{ + return false; +} + + +/******************************************************************* + * Gain API + ******************************************************************/ +std::vector<std::string> SoapyDummy::listGains( const int direction, const size_t channel ) const +{ + std::vector<std::string> options; + options.push_back( "GAIN" ); + + return options; +} + + +void SoapyDummy::setGainMode( const int direction, const size_t channel, const bool automatic ) +{ + /* enable AGC if the hardware supports it, or remove this function */ +} + + +bool SoapyDummy::getGainMode( const int direction, const size_t channel ) const +{ + return false; +} + + +void SoapyDummy::setGain( const int direction, const size_t channel, const double value ) +{ + if ( direction == SOAPY_SDR_RX ) { + } + else if ( direction == SOAPY_SDR_TX ) { + } +} + +void SoapyDummy::setGain( const int direction, const size_t channel, const std::string &name, const double value ) +{ + +} + + +double SoapyDummy::getGain( const int direction, const size_t channel, const std::string &name ) const +{ + return 10.0; +} + + +SoapySDR::Range SoapyDummy::getGainRange( const int direction, const size_t channel, const std::string &name ) const +{ + if (name == "GAIN") { + return(SoapySDR::Range( 0, 60 ) ); + } + return(SoapySDR::Range( 0, 0 ) ); +} + + +/******************************************************************* + * Frequency API + ******************************************************************/ +void SoapyDummy::setFrequency( const int direction, const size_t channel, const std::string &name, const double frequency, const SoapySDR::Kwargs &args ) +{ + if ( name != "RF" ) { + throw std::runtime_error( "setFrequency(" + name + ") unknown name" ); + } + + if (direction==SOAPY_SDR_RX){ + m_frequency = frequency; + } + else if (direction==SOAPY_SDR_TX){ + m_frequency = frequency; + } +} + + +double SoapyDummy::getFrequency( const int direction, const size_t channel, const std::string &name ) const +{ + if ( name != "RF" ) { + throw std::runtime_error( "getFrequency(" + name + ") unknown name" ); + } + + double freq = 0.0; + + if (direction==SOAPY_SDR_RX){ + freq = m_frequency; + } + else if (direction==SOAPY_SDR_TX){ + freq = m_frequency; + } + return freq; +} + +SoapySDR::ArgInfoList SoapyDummy::getFrequencyArgsInfo(const int direction, const size_t channel) const +{ + SoapySDR::ArgInfoList freqArgs; + return freqArgs; +} + +std::vector<std::string> SoapyDummy::listFrequencies( const int direction, const size_t channel ) const +{ + std::vector<std::string> names; + names.push_back( "RF" ); + return names; +} + +SoapySDR::RangeList SoapyDummy::getFrequencyRange( const int direction, const size_t channel, const std::string &name ) const +{ + if ( name != "RF" ) + throw std::runtime_error( "getFrequencyRange(" + name + ") unknown name" ); + return(SoapySDR::RangeList( 1, SoapySDR::Range( 0, 7250000000ull ) ) ); +} + +/******************************************************************* + * Sample Rate API + ******************************************************************/ +void SoapyDummy::setSampleRate( const int direction, const size_t channel, const double rate ) +{ + m_samplerate = rate; +} + +double SoapyDummy::getSampleRate( const int direction, const size_t channel ) const +{ + return m_samplerate; +} + +std::vector<double> SoapyDummy::listSampleRates( const int direction, const size_t channel ) const +{ + std::vector<double> options; + for ( double r = 1e6; r <= 20e6; r += 1e6 ) { + options.push_back( r ); + } + return options; +} + + +void SoapyDummy::setBandwidth( const int direction, const size_t channel, const double bw ) +{ +} + + +double SoapyDummy::getBandwidth( const int direction, const size_t channel ) const +{ + return 8000000; +} + +std::vector<double> SoapyDummy::listBandwidths( const int direction, const size_t channel ) const +{ + std::vector<double> options; + options.push_back( 8000000 ); + return(options); +} diff --git a/Dummy_Streaming.cpp b/Dummy_Streaming.cpp new file mode 100644 index 0000000..493800d --- /dev/null +++ b/Dummy_Streaming.cpp @@ -0,0 +1,296 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Matthias P. Braendli + * + * 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 "SoapyDummy.hpp" +#include <SoapySDR/Logger.hpp> +#include <SoapySDR/Formats.hpp> +#include <chrono> +#include <thread> +#include <algorithm> + +std::vector<std::string> SoapyDummy::getStreamFormats(const int direction, const size_t channel) const +{ + std::vector<std::string> formats; + + formats.push_back(SOAPY_SDR_CS8); + formats.push_back(SOAPY_SDR_CS16); + formats.push_back(SOAPY_SDR_CF32); + formats.push_back(SOAPY_SDR_CF64); + + return formats; +} + +std::string SoapyDummy::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const +{ + fullScale = 1.0; + return SOAPY_SDR_CF32; +} + +SoapySDR::ArgInfoList SoapyDummy::getStreamArgsInfo(const int direction, const size_t channel) const +{ + SoapySDR::ArgInfoList streamArgs; + + SoapySDR::ArgInfo buffersArg; + buffersArg.key="buffers"; + buffersArg.value = std::to_string(1); + buffersArg.name = "Buffer Count"; + buffersArg.description = "Number of buffers per read."; + buffersArg.units = "buffers"; + buffersArg.type = SoapySDR::ArgInfo::INT; + streamArgs.push_back(buffersArg); + + return streamArgs; +} + +SoapySDR::Stream* SoapyDummy::setupStream( + const int direction, + const std::string &format, + const std::vector<size_t> &channels, + const SoapySDR::Kwargs &args ) +{ + if ( channels.size() > 1 or( channels.size() > 0 and channels.at( 0 ) != 0 ) ) { + throw std::runtime_error( "setupStream invalid channel selection" ); + } + + if(direction==SOAPY_SDR_RX){ + + if ( format == SOAPY_SDR_CS8 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS8." ); + }else if ( format == SOAPY_SDR_CS16 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS16." ); + }else if ( format == SOAPY_SDR_CF32 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF32." ); + }else if(format==SOAPY_SDR_CF64){ + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF64." ); + }else throw std::runtime_error( "setupStream invalid format " + format ); + + return RX_STREAM; + } else if(direction==SOAPY_SDR_TX){ + + if ( format == SOAPY_SDR_CS8 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS8." ); + }else if ( format == SOAPY_SDR_CS16 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CS16." ); + }else if ( format == SOAPY_SDR_CF32 ) + { + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF32." ); + }else if(format==SOAPY_SDR_CF64){ + SoapySDR_log( SOAPY_SDR_DEBUG, "Using format CF64." ); + }else throw std::runtime_error( "setupStream invalid format " + format ); + + return TX_STREAM; + } + else { + throw std::runtime_error("Invalid direction"); + } +} + +void SoapyDummy::closeStream( SoapySDR::Stream *stream ) +{ + if (stream == RX_STREAM) { + } else if (stream == TX_STREAM) { + } +} + + +size_t SoapyDummy::getStreamMTU( SoapySDR::Stream *stream ) const +{ + if(stream == RX_STREAM){ + return m_rxstream.mtu; + } else if(stream == TX_STREAM){ + return m_txstream.mtu; + } else { + throw std::runtime_error("Invalid stream"); + } +} + +int SoapyDummy::activateStream( + SoapySDR::Stream *stream, + const int flags, + const long long timeNs, + const size_t numElems ) +{ + if (stream == RX_STREAM) { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Start RX"); + } + else if (stream == TX_STREAM) { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Start TX"); + } + + return 0; +} + + +int SoapyDummy::deactivateStream( + SoapySDR::Stream *stream, + const int flags, + const long long timeNs ) +{ + + if (stream == RX_STREAM) { + } + else if (stream == TX_STREAM) { + } + return 0; +} + + +int SoapyDummy::readStream( + SoapySDR::Stream *stream, + void * const *buffs, + const size_t numElems, + int &flags, + long long &timeNs, + const long timeoutUs ) +{ + if (stream != RX_STREAM){ + return SOAPY_SDR_NOT_SUPPORTED; + } + + size_t returnedElems = std::min(numElems,this->getStreamMTU(stream)); + return returnedElems; +} + +int SoapyDummy::writeStream( + SoapySDR::Stream *stream, + const void * const *buffs, + const size_t numElems, + int &flags, + const long long timeNs, + const long timeoutUs ) +{ + if (stream != TX_STREAM){ + return SOAPY_SDR_NOT_SUPPORTED; + } + + size_t returnedElems = std::min(numElems,this->getStreamMTU(stream)); + + return returnedElems; +} + + +int SoapyDummy::readStreamStatus( + SoapySDR::Stream *stream, + size_t &chanMask, + int &flags, + long long &timeNs, + const long timeoutUs + ){ + + if(stream != TX_STREAM){ + return SOAPY_SDR_NOT_SUPPORTED; + } + + return SOAPY_SDR_TIMEOUT; +} + +int SoapyDummy::acquireReadBuffer( + SoapySDR::Stream *stream, + size_t &handle, + const void **buffs, + int &flags, + long long &timeNs, + const long timeoutUs) +{ + if (stream != RX_STREAM){ + return SOAPY_SDR_NOT_SUPPORTED; + } + + return this->getStreamMTU(stream); +} + +void SoapyDummy::releaseReadBuffer( + SoapySDR::Stream *stream, + const size_t handle) +{ + if(stream != RX_STREAM){ + throw std::runtime_error("Invalid stream"); + } +} + +int SoapyDummy::acquireWriteBuffer( + SoapySDR::Stream *stream, + size_t &handle, + void **buffs, + const long timeoutUs) +{ + + if(stream != TX_STREAM){ + return SOAPY_SDR_NOT_SUPPORTED; + } + + return this->getStreamMTU(stream); +} + +void SoapyDummy::releaseWriteBuffer( + SoapySDR::Stream *stream, + const size_t handle, + const size_t numElems, + int &flags, + const long long timeNs) +{ + if (stream == TX_STREAM) { + } + else { + throw std::runtime_error("Invalid stream"); + } +} + +size_t SoapyDummy::getNumDirectAccessBuffers( + SoapySDR::Stream *stream) +{ + if (stream == RX_STREAM) { + return m_rxstream.buf_num; + } + else if(stream == TX_STREAM){ + return m_rxstream.buf_num; + } + else { + throw std::runtime_error("Invalid stream"); + } +} + +int SoapyDummy::getDirectAccessBufferAddrs( + SoapySDR::Stream *stream, + const size_t handle, + void **buffs) +{ + + if (stream == RX_STREAM) { + buffs[0]=(void *)m_rxstream.buf; + } + else if (stream == TX_STREAM) { + buffs[0]=(void *)m_txstream.buf; + } + else { + throw std::runtime_error("Invalid stream"); + } + + return 0; +} @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2018 Matthias P. Braendli + +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. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..d778870 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +SoapySDR Dummy driver +===================== + +This is a dummy driver for SoapySDR. + +Dependencies +------------ + +* SoapySDR - https://github.com/pothosware/SoapySDR/wiki + + +Licensing information +--------------------- + +The MIT License (MIT) + +Copyright (c) 2018 Matthias P. Braendli + +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. + diff --git a/SoapyDummy.hpp b/SoapyDummy.hpp new file mode 100644 index 0000000..e1ca79a --- /dev/null +++ b/SoapyDummy.hpp @@ -0,0 +1,207 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2018 Matthias P. Braendli + * + * 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 <string.h> +#include <mutex> +#include <condition_variable> +#include <SoapySDR/Device.hpp> +#include <SoapySDR/Logger.hpp> + +class SoapyDummySession +{ + public: + SoapyDummySession(void); + ~SoapyDummySession(void); +}; + +class SoapyDummy : public SoapySDR::Device +{ + public: + SoapyDummy( const SoapySDR::Kwargs & args ); + SoapyDummy(const SoapyDummy& other) = delete; + SoapyDummy& operator=(const SoapyDummy& other) = delete; + ~SoapyDummy( 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; + bool getFullDuplex( const int direction, const size_t channel ) const; + + /******************************************************************* + * Stream API + ******************************************************************/ + std::vector<std::string> 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<size_t> &channels = std::vector<size_t>(), + 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 ); + + int writeStream( + SoapySDR::Stream *stream, + const void * const *buffs, + const size_t numElems, + int &flags, + const long long timeNs = 0, + const long timeoutUs = 100000); + + int readStreamStatus( + SoapySDR::Stream *stream, + size_t &chanMask, + int &flags, + long long &timeNs, + const long timeoutUs + ); + + 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); + + int acquireWriteBuffer( + SoapySDR::Stream *stream, + size_t &handle, + void **buffs, + const long timeoutUs = 100000); + + void releaseWriteBuffer( + SoapySDR::Stream *stream, + const size_t handle, + const size_t numElems, + int &flags, + const long long timeNs = 0); + + size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); + int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); + + /******************************************************************* + * 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; + + /******************************************************************* + * Antenna API + ******************************************************************/ + std::vector<std::string> 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<std::string> listGains( 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; + SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; + std::vector<std::string> listFrequencies( const int direction, const size_t channel ) const; + SoapySDR::RangeList getFrequencyRange( const int direction, const size_t channel, const std::string &name ) 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<double> 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<double> listBandwidths( const int direction, const size_t channel ) const; + + /******************************************************************* + * Dummy callback + ******************************************************************/ + int dummy_tx_callback( int8_t *buffer, int32_t length ); + int dummy_rx_callback( int8_t *buffer, int32_t length ); + + private: + struct DummyStream { + size_t mtu = 1024; + size_t buf_num = 1; + std::complex<float> buf[1024]; + }; + + DummyStream m_rxstream; + DummyStream m_txstream; + SoapySDR::Stream* const TX_STREAM = (SoapySDR::Stream*) 0x1; + SoapySDR::Stream* const RX_STREAM = (SoapySDR::Stream*) 0x2; + + SoapyDummySession m_session; + double m_samplerate = 0; + double m_frequency = 0; +}; diff --git a/self_test.py b/self_test.py new file mode 100644 index 0000000..e18a933 --- /dev/null +++ b/self_test.py @@ -0,0 +1,81 @@ +import SoapySDR +from SoapySDR import * #SOAPY_SDR_* constants +import numpy as np +import time + +if __name__ == "__main__": + dummy = SoapySDR.Device(dict(driver="dummy")) + print(dummy) + + dummy.setSampleRate(SOAPY_SDR_RX, 0, 8e6) + dummy.setSampleRate(SOAPY_SDR_TX, 0, 8e6) + + """ + for i in range(5): + print(" Make rx stream #%d"%i) + rxStream = dummy.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0]) + for j in range(5): + numSampsTotal = 10000 + print(" Activate, get %d samples, Deactivate #%d"%(numSampsTotal, j)) + dummy.activateStream(rxStream) + buff = np.array([0]*1024, np.complex64) + while numSampsTotal > 0: + sr = dummy.readStream(rxStream, [buff], buff.size, timeoutUs=int(1e6)) + #print sr + assert(sr.ret > 0) + numSampsTotal -= sr.ret + dummy.deactivateStream(rxStream) + dummy.closeStream(rxStream) + + for i in range(5): + print(" Make tx stream #%d"%i) + txStream = dummy.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0]) + for j in range(5): + numSampsTotal = 10000 + print(" Activate, send %d samples, Deactivate #%d"%(numSampsTotal, j)) + dummy.activateStream(txStream) + buff = np.array([0]*1024, np.complex64) + while numSampsTotal != 0: + size = min(buff.size, numSampsTotal) + sr = dummy.writeStream(txStream, [buff], size) + #print sr + if not (sr.ret > 0): print("Fail %s, %d"%(sr, numSampsTotal)) + assert(sr.ret > 0) + numSampsTotal -= sr.ret + dummy.deactivateStream(txStream) + dummy.closeStream(txStream) + """ + + #################################################################### + #setup both streams at once + #################################################################### + rxStream = dummy.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [0]) + txStream = dummy.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CF32, [0]) + + dummy.activateStream(rxStream) + dummy.activateStream(txStream) + + numSampsTotal = 10000 + dummy.activateStream(rxStream) + buff = np.array([0]*1024, np.complex64) + while numSampsTotal > 0: + sr = dummy.readStream(rxStream, [buff], buff.size, timeoutUs=int(1e6)) + #print(sr) + assert(sr.ret > 0) + numSampsTotal -= sr.ret + + numSampsTotal = 10000 + buff = np.array([0]*1024, np.complex64) + while numSampsTotal != 0: + size = min(buff.size, numSampsTotal) + sr = dummy.writeStream(txStream, [buff], size) + #print(sr) + if not (sr.ret > 0): print("Fail %s, %d"%(sr, numSampsTotal)) + assert(sr.ret > 0) + numSampsTotal -= sr.ret + + dummy.deactivateStream(rxStream) + dummy.deactivateStream(txStream) + + dummy.closeStream(rxStream) + dummy.closeStream(txStream) |