diff --git a/.gitignore b/.gitignore index c6127b3..96c3260 100644 --- a/.gitignore +++ b/.gitignore @@ -50,3 +50,10 @@ modules.order Module.symvers Mkfile.old dkms.conf + +#unique things to ignore +*.swp +.cache/* +build/* +*.json + diff --git a/CMakeLists.txt b/CMakeLists.txt index 2be88dd..01d8814 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ if (NOT Sidekiq_FOUND) endif () message(STATUS "Sidekiq_INCLUDE_DIRS - ${Sidekiq_INCLUDE_DIRS}") message(STATUS "Sidekiq_LIBRARIES - ${Sidekiq_LIBRARIES}") +message(STATUS "OTHER_LIBS - ${OTHER_LIBS}") include_directories(${CMAKE_CURRENT_SOURCE_DIR}) include_directories(${Sidekiq_INCLUDE_DIRS}) @@ -61,4 +62,4 @@ SOAPY_SDR_MODULE_UTIL( /usr/lib/epiq/libusb-1.0.so /usr/lib/epiq/libglib-2.0.so -lrt -) \ No newline at end of file +) diff --git a/FindSidekiq.cmake b/FindSidekiq.cmake index 692e12d..6525b39 100644 --- a/FindSidekiq.cmake +++ b/FindSidekiq.cmake @@ -10,22 +10,58 @@ if(NOT Sidekiq_FOUND) find_path(Sidekiq_INCLUDE_DIR NAMES sidekiq_api.h HINTS ${Sidekiq_PKG_INCLUDE_DIRS} $ENV{Sidekiq_DIR}/include - PATHS /usr/local/include /usr/include /opt/include /opt/local/include) + PATHS ~/sidekiq_sdk_current/sidekiq_core/inc/ /usr/local/include /usr/include /opt/include /opt/local/include) + + execute_process ( + COMMAND uname -m + OUTPUT_VARIABLE outVar + ) + + message(STATUS "type ${outVar}") + + if(${outVar} MATCHES "x86_64") + set (libname "libsidekiq__x86_64.gcc.a") + set (otherlib "none") + else() + set(libname "libsidekiq__aarch64.gcc6.3.a") + set(otherlib "libiio.so") + endif() + + message(STATUS "library is ${libname} ") + message(STATUS "otherlib is ${otherlib} ") find_library(Sidekiq_LIBRARY - NAMES libsidekiq__x86_64.gcc.a - HINTS ${Sidekiq_PKG_LIBRARY_DIRS} $ENV{Sidekiq_DIR}/include - PATHS /usr/local/lib /usr/lib /opt/lib /opt/local/lib) + NAMES ${libname} + HINTS ${Sidekiq_PKG_LIBRARY_DIRS} $ENV{Sidekiq_DIR}/include + PATHS ~/sidekiq_sdk_current/lib/ /usr/local/lib /usr/lib /opt/lib /opt/local/lib) + set(Sidekiq_LIBRARIES ${Sidekiq_LIBRARY}) set(Sidekiq_INCLUDE_DIRS ${Sidekiq_INCLUDE_DIR}) - include(FindPackageHandleStandardArgs) - # handle the QUIETLY and REQUIRED arguments and set LibSidekiq_FOUND to TRUE - # if all listed variables are TRUE - find_package_handle_standard_args(Sidekiq DEFAULT_MSG - Sidekiq_LIBRARY Sidekiq_INCLUDE_DIR) + if(${otherlib} MATCHES "libiio.so") + find_library(OTHER_LIBS + NAMES ${otherlib} + HINTS ${Sidekiq_PKG_LIBRARY_DIRS} $ENV{Sidekiq_DIR}/include + PATHS /usr/lib/epiq/ /usr/local/lib /usr/lib /opt/lib /opt/local/lib) + + set(OTHER_LIBS ${OTHER_LIBS}) + + include(FindPackageHandleStandardArgs) + # handle the QUIETLY and REQUIRED arguments and set LibSidekiq_FOUND to TRUE + # if all listed variables are TRUE + find_package_handle_standard_args(Sidekiq DEFAULT_MSG + Sidekiq_LIBRARY Sidekiq_INCLUDE_DIR OTHER_LIBS) + + mark_as_advanced(Sidekiq_INCLUDE_DIR Sidekiq_LIBRARY OTHER_LIBS) + else() + include(FindPackageHandleStandardArgs) + # handle the QUIETLY and REQUIRED arguments and set LibSidekiq_FOUND to TRUE + # if all listed variables are TRUE + find_package_handle_standard_args(Sidekiq DEFAULT_MSG + Sidekiq_LIBRARY Sidekiq_INCLUDE_DIR ) - mark_as_advanced(Sidekiq_INCLUDE_DIR Sidekiq_LIBRARY) + mark_as_advanced(Sidekiq_INCLUDE_DIR Sidekiq_LIBRARY ) + endif() endif(NOT Sidekiq_FOUND) diff --git a/Registation.cpp b/Registation.cpp index 0526dd2..25d8f33 100644 --- a/Registation.cpp +++ b/Registation.cpp @@ -1,72 +1,102 @@ // Copyright [2018] " -#include #include "SoapySidekiq.hpp" #include +#include +#include +#include -static std::vector findSidekiq(const SoapySDR::Kwargs &args) { - std::vector results; - - uint8_t number_of_cards = 0; - uint8_t card_list[SKIQ_MAX_NUM_CARDS]; - char *serial_str; - pid_t card_owner; - skiq_xport_type_t type = skiq_xport_type_auto; +static std::vector findSidekiq(const SoapySDR::Kwargs &args) +{ + int status = 0; + std::vector results; - /* query the list of all Sidekiq cards on the PCIe interface */ - if(skiq_get_cards(type, &number_of_cards, card_list) != 0){ - SoapySDR_log(SOAPY_SDR_ERROR, "Failure: skiq_get_cards"); - } + SoapySDR::setLogLevel(SOAPY_SDR_TRACE); + SoapySDR_logf(SOAPY_SDR_TRACE, "findSidekiq"); - for (int i = 0; i < number_of_cards; i++) { - SoapySDR::Kwargs devInfo; - bool deviceAvailable = false; + uint8_t number_of_cards = 0; + uint8_t card_list[SKIQ_MAX_NUM_CARDS]; + char * serial_str; + pid_t card_owner; + skiq_xport_type_t type = skiq_xport_type_auto; - /* determine the serial number based on the card number */ - if(skiq_read_serial_string(i, &serial_str) != 0){ - SoapySDR_log(SOAPY_SDR_ERROR, "Failure: skiq_read_serial_string"); + /* query the list of all Sidekiq cards on the PCIe interface */ + status = skiq_get_cards(type, &number_of_cards, card_list); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_get_cards, status %d", + status); } - /* get card availability */ - skiq_is_card_avail(i, &card_owner); - deviceAvailable = (card_owner == getpid()); // owner must be this process(pid) - if (!deviceAvailable) { - SoapySDR_logf(SOAPY_SDR_WARNING, "Unable to access card #%d, owner pid (%d)", i, card_owner); - } + for (int i = 0; i < number_of_cards; i++) + { + SoapySDR::Kwargs devInfo; + bool deviceAvailable = false; - std::string deviceLabel = "Epiq Solutions - Sidekiq :: " + std::string(serial_str); + /* determine the serial number based on the card number */ + status = skiq_read_serial_string(card_list[i], &serial_str); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_serial_string, status %d", + status); + } - devInfo["card"] = std::to_string(i); - devInfo["label"] = deviceLabel; - devInfo["available"] = deviceAvailable ? "Yes" : "No"; - devInfo["product"] = "Sidekiq"; - devInfo["serial"] = std::string(serial_str); - devInfo["manufacturer"] = "Epiq Solutions"; - SoapySidekiq::sidekiq_devices.push_back(devInfo); - } + /* get card availability */ + skiq_is_card_avail(card_list[i], &card_owner); + deviceAvailable = + (card_owner == getpid()); // owner must be this process(pid) + if (!deviceAvailable) + { + SoapySDR_logf(SOAPY_SDR_WARNING, + "Unable to access card #%d, owner pid (%d)", card_list[i], + card_owner); + } + + std::string deviceLabel = + "Epiq Solutions - Sidekiq :: " + std::string(serial_str); + + devInfo["card"] = std::to_string(card_list[i]); + devInfo["label"] = deviceLabel; + devInfo["available"] = deviceAvailable ? "Yes" : "No"; + devInfo["product"] = "Sidekiq"; + devInfo["serial"] = std::string(serial_str); + devInfo["manufacturer"] = "Epiq Solutions"; + SoapySidekiq::sidekiq_devices.push_back(devInfo); + } - // filtering - for (int i = 0; i < number_of_cards; i++) { - SoapySDR::Kwargs devInfo = SoapySidekiq::sidekiq_devices[i]; - if (args.count("card") != 0) { - if (args.at("card") != devInfo.at("card")) { - continue; - } - SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by card %s", devInfo.at("card").c_str()); - } else if (args.count("serial") != 0) { - if (devInfo.at("serial") != args.at("serial")) { - continue; - } - SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by serial %s", args.at("serial").c_str()); + // filtering + for (int i = 0; i < number_of_cards; i++) + { + SoapySDR::Kwargs devInfo = SoapySidekiq::sidekiq_devices[i]; + if (args.count("card") != 0) + { + if (args.at("card") != devInfo.at("card")) + { + continue; + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by card %s", + devInfo.at("card").c_str()); + } + else if (args.count("serial") != 0) + { + if (devInfo.at("serial") != args.at("serial")) + { + continue; + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Found device by serial %s", + args.at("serial").c_str()); + } + results.push_back(SoapySidekiq::sidekiq_devices[i]); } - results.push_back(SoapySidekiq::sidekiq_devices[i]); - } - return results; + return results; } -static SoapySDR::Device *makeSidekiq(const SoapySDR::Kwargs &args) { - return new SoapySidekiq(args); +static SoapySDR::Device *makeSidekiq(const SoapySDR::Kwargs &args) +{ + return new SoapySidekiq(args); } -static SoapySDR::Registry registerSidekiq("sidekiq", &findSidekiq, &makeSidekiq, SOAPY_SDR_ABI_VERSION); +static SoapySDR::Registry registerSidekiq("sidekiq", &findSidekiq, &makeSidekiq, + SOAPY_SDR_ABI_VERSION); diff --git a/Sensor.cpp b/Sensor.cpp index 28ecd6e..ce214ba 100644 --- a/Sensor.cpp +++ b/Sensor.cpp @@ -2,57 +2,121 @@ #include "SoapySidekiq.hpp" -std::vector SoapySidekiq::listSensors(void) const { - std::vector sensors; - sensors.push_back("temperature"); - sensors.push_back("acceleration"); - return sensors; +std::vector SoapySidekiq::listSensors(void) const +{ + std::vector sensors; + SoapySDR_logf(SOAPY_SDR_TRACE, "listSensors"); + + sensors.push_back("temperature"); + sensors.push_back("acceleration"); + + return sensors; } -SoapySDR::ArgInfo SoapySidekiq::getSensorInfo(const std::string &key) const { - return SoapySDR::Device::getSensorInfo(key); +SoapySDR::ArgInfo SoapySidekiq::getSensorInfo(const std::string &key) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getSensorInfo, key %s", key.c_str()); + + return SoapySDR::Device::getSensorInfo(key); } -std::string SoapySidekiq::readSensor(const std::string &key) const { - if (key.compare("temperature")) { - int8_t temp = 0; - if (skiq_read_temp(card, &temp) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_temp (card %i)", card); - } - return std::to_string(temp); - } - bool supported = false; - if (key.compare("acceleration")) { - if (!skiq_is_accel_supported(card, &supported)) { - SoapySDR_logf(SOAPY_SDR_WARNING, "Acceleration not supported by card %i", card); - return "{}"; - } - if (skiq_write_accel_state(card, 1) != 0) {// enable - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_accel_state (card %i)", card); - return "{}"; +std::string SoapySidekiq::readSensor(const std::string &key) const +{ + int status = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "readSensor, key: '%s'", key.c_str()); + + if (key.compare("temperature") == 0) + { + int8_t temp = 0; + status = skiq_read_temp(card, &temp); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_temp (card %i), status %d", card, + status); + } + else + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Temp is %d", temp); + } + + return std::to_string(temp); } - int16_t x_data = 0; - int16_t y_data = 0; - int16_t z_data = 0; - if (skiq_read_accel(card, &x_data, &y_data, &z_data) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_accel (card %i)", card); - return "{}"; + bool supported = false; + + if (key.compare("acceleration")== 0) + { + status = skiq_is_accel_supported(card, &supported); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq-is_accel_supported (card %i), status %d", card, + status); + } + + if (!supported) + { + SoapySDR_logf(SOAPY_SDR_WARNING, + "Acceleration not supported by card %i, status %d", + card, status); + return "{}"; + } + + /* enable accel for the card */ + status = skiq_write_accel_state(card, 1); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_accel_state (card %i), status %d", card, + status); + return "{}"; + } + + int16_t x_data = 0; + int16_t y_data = 0; + int16_t z_data = 0; + status = skiq_read_accel(card, &x_data, &y_data, &z_data); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_accel (card %i), status %d", card, + status); + return "{}"; + } + + /* disable accel */ + status = skiq_write_accel_state(card, 0); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_accel_state (card %i), status %d", card, + status); + return "{}"; + }; + std::stringstream ss; + ss << "{\"x\":" << x_data << " \"y\":" << y_data << " \"z\":" << z_data + << "}"; // json format + + SoapySDR_logf(SOAPY_SDR_DEBUG, "accel data %s", (ss.str().c_str())); + return ss.str(); } - if (skiq_write_accel_state(card, 0) != 0) { // disable - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_accel_state (card %i)", card); - return "{}"; - }; - std::stringstream ss; - ss << "{\"x\":" << x_data << " \"y\":" << y_data << " \"z\":" << z_data << "}"; // json format - return ss.str(); - } - - return SoapySDR::Device::readSensor(key); + + SoapySDR_log(SOAPY_SDR_DEBUG, "sensor didn't match"); + return SoapySDR::Device::readSensor(key); } -std::vector SoapySidekiq::listSensors(const int direction, const size_t channel) const { - return SoapySDR::Device::listSensors(direction, channel); +std::vector SoapySidekiq::listSensors(const int direction, + const size_t channel) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "listSensors2"); + return SoapySDR::Device::listSensors(direction, channel); } -std::string SoapySidekiq::readSensor(const int direction, const size_t channel, const std::string &key) const { - return SoapySDR::Device::readSensor(direction, channel, key); +std::string SoapySidekiq::readSensor(const int direction, const size_t channel, + const std::string &key) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "read_sensor2"); + return SoapySDR::Device::readSensor(direction, channel, key); } diff --git a/Settings.cpp b/Settings.cpp index a2664ed..fbc5636 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -3,468 +3,1045 @@ #include "SoapySidekiq.hpp" std::vector SoapySidekiq::sidekiq_devices; -bool SoapySidekiq::rx_running; +bool SoapySidekiq::rx_running; + +// Constructor +SoapySidekiq::SoapySidekiq(const SoapySDR::Kwargs &args) +{ + int status = 0; + uint8_t channels = 0; + + /* We need to set some default parameters in case the user does not */ + + SoapySDR::setLogLevel(SOAPY_SDR_TRACE); + + // rx defaults + rx_sample_rate = 2048000; + rx_bandwidth = (uint32_t)(rx_sample_rate * 0.8); + rx_center_frequency = 100000000; + + // tx defaults + tx_sample_rate = 2048000; + tx_bandwidth = (uint32_t)(tx_sample_rate * 0.8); + tx_center_frequency = 100000000; + + useShort = true; + iq_swap = false; + counter = false; + debug_ctr = 0; + card = 0; + + // this may change later according to format + rx_running = false; + + if (args.count("card") != 0) + { + try + { + + card = std::stoi(args.at("card")); + serial = args.at("serial"); + } + catch (const std::invalid_argument &) + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Requested card (%d), not found", std::stoi(args.at("card"))); + } + + SoapySDR_logf( SOAPY_SDR_DEBUG, "Found Sidekiq Device 'card' = %d, 'serial' = %s", card, serial.c_str()); + } + else + { + SoapySDR_logf(SOAPY_SDR_ERROR, "No cards found"); + } -SoapySidekiq::SoapySidekiq(const SoapySDR::Kwargs &args) { - // rx defaults - rx_sample_rate = 2048000; - rx_bandwidth = 2048000; - rx_center_frequency = 100000000; - // tx defaults - tx_sample_rate = 2048000; - tx_bandwidth = 2048000; - tx_center_frequency = 100000000; - iq_swap = false; + rx_hdl = skiq_rx_hdl_A1; + tx_hdl = skiq_tx_hdl_A1; - // this may change later according to format - shortsPerWord = 1; - bufferLength = bufferElems * elementsPerSample * shortsPerWord; + skiq_xport_type_t type = skiq_xport_type_auto; + skiq_xport_init_level_t level = skiq_xport_init_level_full; - bufferedElems = 0; - _currentBuff = 0; - resetBuffer = false; - rx_running = true; - useShort = true; + SoapySDR_logf(SOAPY_SDR_DEBUG, "Sidekiq opening card %d", card); - if (args.count("card") != 0) { - try { - card = std::stoi(args.at("card")); + /* init sidekiq */ + status = skiq_init(type, level, &card, 1); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_init (card %d), status %d", card, status); } - catch (const std::invalid_argument &) { + /* set iq order to iq instead of qi */ + status = skiq_write_iq_order_mode(card, skiq_iq_order_iq); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: setting iq order (card %d), status %d", card, + status); } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting iq mode"); + + rx_block_size_in_words = 0; - SoapySDR_logf(SOAPY_SDR_DEBUG, "Found Sidekiq Device using device index parameter 'card' = %d", card); - } + status = skiq_read_num_rx_chans(card, &channels); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "failure reading rx number of channels (card %d), status %d", card, status); + } + num_rx_channels = channels; - // Handle (TODO add to args) - rx_hdl = skiq_rx_hdl_A1; - tx_hdl = skiq_tx_hdl_A1; - skiq_xport_type_t type = skiq_xport_type_auto; - skiq_xport_init_level_t level = skiq_xport_init_level_full; + status = skiq_read_num_tx_chans(card, &channels); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "failure reading tx number of channels (card %d), status %d", card, status); + } + num_tx_channels = channels; + uint8_t tmp_resolution = 0; + + /* Every card can have a different iq resolution. Some are 12 bits and some are 14 and some 16 + * So to validate the data we need to know when to wrap the value + */ + status = skiq_read_rx_iq_resolution( card, &tmp_resolution ); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Error: failed to read iq_resolution %d (status = %d)", + card, status); + } - SoapySDR_logf(SOAPY_SDR_DEBUG, "Sidekiq opening card %d", card); + this->resolution = tmp_resolution; + this->max_value = (double) ((1 << (tmp_resolution-1))-1); + SoapySDR_logf(SOAPY_SDR_DEBUG, "Info: resolution %d" " bits, max ADC value %d", + this->resolution, (uint64_t) this->max_value); - /* init sidekiq */ - if (skiq_init(type, level, &card, 1) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_init (card %d)", card); - } } -SoapySidekiq::~SoapySidekiq(void) { - if (skiq_exit() != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_exit", card); - } +SoapySidekiq::~SoapySidekiq(void) +{ + + SoapySDR_logf(SOAPY_SDR_INFO, "In destructor", card); + if (skiq_exit() != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_exit", card); + } + SoapySDR_logf(SOAPY_SDR_INFO, "leaving destructor", card); } /******************************************************************* * Identification API ******************************************************************/ -std::string SoapySidekiq::getDriverKey(void) const { - return "Sidekiq"; +std::string SoapySidekiq::getDriverKey(void) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getDriverKey"); + return "Sidekiq"; } -std::string SoapySidekiq::getHardwareKey(void) const { - return "Sidekiq"; +std::string SoapySidekiq::getHardwareKey(void) const +{ + int status = 0; + skiq_param_t param; + std::string part_str = "none"; + skiq_part_t part; + + SoapySDR_logf(SOAPY_SDR_TRACE, "getHardwareKey"); + // query card name and return + + status = skiq_read_parameters(card, ¶m); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: card %d, skiq_read_parameters status is %d", + card, status); + } + + part = param.card_param.part_type; + part_str = skiq_part_string(part); + + SoapySDR_logf(SOAPY_SDR_DEBUG, "Part type is %s", part_str.c_str()); + + return part_str; } -SoapySDR::Kwargs SoapySidekiq::getHardwareInfo(void) const { - // key/value pairs for any useful information - // this also gets printed in --probe - SoapySDR::Kwargs args; +SoapySDR::Kwargs SoapySidekiq::getHardwareInfo(void) const +{ + // key/value pairs for any useful information + // this also gets printed in --probe + SoapySDR::Kwargs args; + SoapySDR_logf(SOAPY_SDR_TRACE, "getHardwareInfo"); - args["origin"] = "https://github.com/pothosware/SoapySidekiq"; - args["card"] = std::to_string(card); + args["origin"] = "https://github.com/pothosware/SoapySidekiq"; + args["card"] = std::to_string(card); + args["serial"] = serial; + args["rx_channels"] = std::to_string(num_rx_channels); + args["tx_channels"] = std::to_string(num_tx_channels); - return args; + return args; } /******************************************************************* * Channels API ******************************************************************/ -size_t SoapySidekiq::getNumChannels(const int dir) const { - return 1; +size_t SoapySidekiq::getNumChannels(const int dir) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getNumChannels, direction = %d", dir); + + if (dir == SOAPY_SDR_RX) + { + return num_rx_channels; + } + else if (dir == SOAPY_SDR_TX) + { + return num_tx_channels; + } + else + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Failure getNumChannels called for an Invalid Direction %d", dir); + } + + return -1; } /******************************************************************* - * Antenna API + * Frontend corrections API ******************************************************************/ -std::vector SoapySidekiq::listAntennas(const int direction, const size_t channel) const { - std::vector antennas; - antennas.push_back("RX"); - antennas.push_back("TX"); - return antennas; +bool SoapySidekiq::hasDCOffsetMode(const int direction, + const size_t channel) const +{ + int status = 0; + uint32_t mask = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "hasDCOffsetMode direction = %d", direction); + + + if (direction == SOAPY_SDR_RX) + { + skiq_rx_hdl_t hdl = static_cast(channel); + status = skiq_read_rx_cal_types_avail(card, hdl, &mask); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_rx_cal_types_avail (card %d, " + "mask %d), status %d", + card, mask, status); + } + + if ((mask & skiq_rx_cal_type_dc_offset)== skiq_rx_cal_type_dc_offset) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Channel %d, has DC Offest Correction %d", channel, mask); + return true; + } + else + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Channel %d, does not have DC Offest Correction %d", channel, mask); + return false; + } + } + else + { + //TODO + } + + return false; } -void SoapySidekiq::setAntenna(const int direction, const size_t channel, const std::string &name) { - SoapySDR::Device::setAntenna(direction, channel, name); +void SoapySidekiq::setDCOffsetMode(const int direction, const size_t channel, + const bool automatic) +{ + int status = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "setDCOffsetMode, direction = %d", direction); + + if (direction == SOAPY_SDR_RX) + { + skiq_rx_hdl_t hdl = static_cast(channel); + status = skiq_write_rx_dc_offset_corr(card, hdl, automatic); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_dc_offset_corr (card %d, " + "enable %d), status %d", + card, automatic, status); + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting DC Offest Correction to %d", automatic); + } + else + { + //TODO + } } -std::string SoapySidekiq::getAntenna(const int direction, const size_t channel) const { - return SoapySDR::Device::getAntenna(direction, channel); +bool SoapySidekiq::getDCOffsetMode(const int direction, + const size_t channel) const +{ + bool enable = false; + int status = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "getDCOffsetMode"); + + if (direction == SOAPY_SDR_RX) + { + skiq_rx_hdl_t hdl = static_cast(channel); + status = skiq_read_rx_dc_offset_corr(card, hdl, &enable); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_dc_offset_corr (card %d, " + "enable %d), status %d", + card, enable, status); + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Channel %d, get DC Offest Correction %d", channel, enable); + } + else + { + //TODO + } + + return enable; + } /******************************************************************* - * Frontend corrections API + * Antenna API ******************************************************************/ -bool SoapySidekiq::hasDCOffsetMode(const int direction, const size_t channel) const { - return direction == SOAPY_SDR_RX; +std::vector SoapySidekiq::listAntennas(const int direction, + const size_t channel) const +{ + std::vector antennas; + antennas.push_back(""); + // antennas.push_back("TX"); + return antennas; } -void SoapySidekiq::setDCOffsetMode(const int direction, const size_t channel, const bool automatic) { - if (direction == SOAPY_SDR_RX) { - if (skiq_write_rx_dc_offset_corr(card, rx_hdl, automatic) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_rx_dc_offset_corr (card %d, enable %d)", card, automatic); - } - } +void SoapySidekiq::setAntenna(const int direction, const size_t channel, + const std::string &name) +{ + SoapySDR::Device::setAntenna(direction, channel, name); } -bool SoapySidekiq::getDCOffsetMode(const int direction, const size_t channel) const { - bool enable; - if (direction == SOAPY_SDR_RX) { - if (skiq_read_rx_dc_offset_corr(card, rx_hdl, &enable) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_dc_offset_corr (card %d)", card); - } - return enable; - } - - return SoapySDR::Device::getDCOffsetMode(direction, channel); +std::string SoapySidekiq::getAntenna(const int direction, + const size_t channel) const +{ + return SoapySDR::Device::getAntenna(direction, channel); } /******************************************************************* * Gain API ******************************************************************/ -std::vector SoapySidekiq::listGains(const int direction, const size_t channel) const { - // list available gain elements, - std::vector results; - return results; +std::vector SoapySidekiq::listGains(const int direction, + const size_t channel) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "listGains"); + // list available gain elements, + std::vector results; + return results; } -bool SoapySidekiq::hasGainMode(const int direction, const size_t channel) const { - return true; +bool SoapySidekiq::hasGainMode(const int direction, const size_t channel) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "hasGainMode"); + return true; } -void SoapySidekiq::setGainMode(const int direction, const size_t channel, const bool automatic) { - if (direction == SOAPY_SDR_RX) { - SoapySDR_logf(SOAPY_SDR_DEBUG, - "Setting Sidekiq RX Gain Mode: %s", - automatic ? "skiq_rx_gain_auto" : "skiq_rx_gain_manual"); - skiq_rx_gain_t mode = automatic ? skiq_rx_gain_auto : skiq_rx_gain_manual; - if (skiq_write_rx_gain_mode(card, rx_hdl, mode) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_rx_gain_mode (card %d, mode %d)", card, mode); +void SoapySidekiq::setGainMode(const int direction, const size_t channel, + const bool automatic) +{ + int status = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "setGainMode"); + + if (direction == SOAPY_SDR_RX) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting Sidekiq RX Gain Mode: %s", + automatic ? "skiq_rx_gain_auto" : "skiq_rx_gain_manual"); + skiq_rx_gain_t mode = + automatic ? skiq_rx_gain_auto : skiq_rx_gain_manual; + status = skiq_write_rx_gain_mode(card, rx_hdl, mode); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_gain_mode (card %d, mode " + "%d), status %d", + card, mode, status); + } } - } } -bool SoapySidekiq::getGainMode(const int direction, const size_t channel) const { - if (direction == SOAPY_SDR_RX) { - skiq_rx_gain_t p_gain_mode; - if (skiq_read_rx_gain_mode(card, rx_hdl, &p_gain_mode) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_gain_mode (card %d)", card); +bool SoapySidekiq::getGainMode(const int direction, const size_t channel) const +{ + int status = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "getGainMode"); + + if (direction == SOAPY_SDR_RX) + { + skiq_rx_gain_t p_gain_mode; + status = skiq_read_rx_gain_mode(card, rx_hdl, &p_gain_mode); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_gain_mode (card %d), status", + card, status); + } + return p_gain_mode == skiq_rx_gain_auto; } - return p_gain_mode == skiq_rx_gain_auto; - } - return SoapySDR::Device::getGainMode(direction, channel); + return SoapySDR::Device::getGainMode(direction, channel); } -void SoapySidekiq::setGain(const int direction, const size_t channel, const double value) { - if (direction == SOAPY_SDR_RX) { - if (skiq_write_rx_gain(card, rx_hdl, value) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_rx_gain (card %d, value %d)", card, value); +void SoapySidekiq::setGain(const int direction, const size_t channel, + const double value) +{ + int status; + + SoapySDR_logf(SOAPY_SDR_TRACE, "setGain"); + + if (direction == SOAPY_SDR_RX) + { + /* if gain mode is automatic, we should leave */ + if (!getGainMode(direction, channel)) + { + uint16_t gain = (uint16_t)(abs(value)); + status = skiq_write_rx_gain(card, rx_hdl, gain); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_gain (card %d, value %d) status %d", + card, gain, status); + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx gain: %d", gain); + } + else + { + SoapySDR_log(SOAPY_SDR_WARNING, + "Attempt to set gain even though it" + " was already set to automatic gain mode"); + } + } + + /* For TX gain is attenuation, someone may send that gain as negative or + * positive Assume it is attenuation and take the abs() of the number */ + if (direction == SOAPY_SDR_TX) + { + uint16_t attenuation = (uint16_t)(abs(value)); + status = skiq_write_tx_attenuation(card, tx_hdl, attenuation); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_gain (card %d, value %d), status %d", + card, attenuation, status); + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting tx attenuation: %d", + attenuation); } - } } -double SoapySidekiq::getGain(const int direction, const size_t channel) const { - if (direction == SOAPY_SDR_RX) { - uint8_t gain_index; - if (skiq_read_rx_gain(card, rx_hdl, &gain_index) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_gain (card %d)", card); +double SoapySidekiq::getGain(const int direction, const size_t channel) const +{ + int status = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "getGain"); + + if (direction == SOAPY_SDR_RX) + { + uint8_t gain_index; + status = skiq_read_rx_gain(card, rx_hdl, &gain_index); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_gain (card %d), status %d", + card, status); + } + return static_cast(gain_index); } - return static_cast(gain_index); - } - return SoapySDR::Device::getGain(direction, channel); + return SoapySDR::Device::getGain(direction, channel); } -SoapySDR::Range SoapySidekiq::getGainRange(const int direction, const size_t channel, const std::string &name) const { - if (direction == SOAPY_SDR_RX) { - uint8_t gain_index_min; - uint8_t gain_index_max; - if (skiq_read_rx_gain_index_range(card, rx_hdl, &gain_index_min, &gain_index_max) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_gain_index_range (card %d)", card); +SoapySDR::Range SoapySidekiq::getGainRange(const int direction, + const size_t channel) const +{ + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "getGainRange"); + + if (direction == SOAPY_SDR_RX) + { + uint8_t gain_index_min; + uint8_t gain_index_max; + + status = skiq_read_rx_gain_index_range(card, rx_hdl, &gain_index_min, + &gain_index_max); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_gain_index_range (card %d), status %d", + card); + return SoapySDR::Device::getGainRange(direction, channel); + } + + //TODO convert from gain index to dB + + return SoapySDR::Range(gain_index_min, gain_index_max); } - return SoapySDR::Range(gain_index_max, gain_index_max); - } - return SoapySDR::Device::getGainRange(direction, channel, name); + return SoapySDR::Device::getGainRange(direction, channel); } /******************************************************************* * Frequency API ******************************************************************/ -void SoapySidekiq::setFrequency(const int direction, - const size_t channel, - const std::string &name, - const double frequency, - const SoapySDR::Kwargs &args) { - if (direction == SOAPY_SDR_RX && name == "RF") { - rx_center_frequency = (uint64_t) frequency; - resetBuffer = true; - SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx center freq: %d", rx_center_frequency); - if (skiq_write_rx_LO_freq(card, rx_hdl, rx_center_frequency) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_rx_LO_freq (card %d, frequency %d)", - card, - rx_center_frequency); +void SoapySidekiq::setFrequency(const int direction, const size_t channel, + const std::string &name, const double frequency, + const SoapySDR::Kwargs &args) +{ + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "setFrequency"); + if (direction == SOAPY_SDR_RX && name == "RF") + { + rx_center_frequency = (uint64_t)frequency; + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx center freq: %ld", + rx_center_frequency); + status = skiq_write_rx_LO_freq(card, rx_hdl, rx_center_frequency); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_LO_freq (card %d, frequency " + "%d), status %d", + card, rx_center_frequency, status); + } } - } - if (direction == SOAPY_SDR_TX && name == "RF") { - tx_center_frequency = (uint64_t) frequency; - resetBuffer = true; - SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting tx center freq: %d", tx_center_frequency); - if (skiq_write_tx_LO_freq(card, tx_hdl, tx_center_frequency) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_tx_LO_freq (card %d, frequency %d)", - card, - tx_center_frequency); + if (direction == SOAPY_SDR_TX && name == "RF") + { + tx_center_frequency = (uint64_t)frequency; + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting tx center freq: %d", + tx_center_frequency); + status = skiq_write_tx_LO_freq(card, tx_hdl, tx_center_frequency); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_LO_freq (card %d, frequency " + "%d), status %d", + card, tx_center_frequency, status); + } } - } } -double SoapySidekiq::getFrequency(const int direction, const size_t channel, const std::string &name) const { - if (direction == SOAPY_SDR_RX && name == "RF") { - uint64_t freq; - double tuned_freq; - if (skiq_read_rx_LO_freq(card, rx_hdl, &freq, &tuned_freq) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_LO_freq (card %d)", card); +double SoapySidekiq::getFrequency(const int direction, const size_t channel, + const std::string &name) const +{ + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "getFrequency"); + if (direction == SOAPY_SDR_RX && name == "RF") + { + uint64_t freq; + double tuned_freq; + status = skiq_read_rx_LO_freq(card, rx_hdl, &freq, &tuned_freq); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_LO_freq (card %d), status %d", + card, status); + } + return static_cast(freq); } - return static_cast(freq); - } - if (direction == SOAPY_SDR_TX && name == "RF") { - uint64_t freq; - double tuned_freq; - if (skiq_read_tx_LO_freq(card, tx_hdl, &freq, &tuned_freq) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_tx_LO_freq (card %d)", card); + if (direction == SOAPY_SDR_TX && name == "RF") + { + uint64_t freq; + double tuned_freq; + status = skiq_read_tx_LO_freq(card, tx_hdl, &freq, &tuned_freq); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_tx_LO_freq (card %d), status %d", + card, status); + } + return static_cast(freq); } - return static_cast(freq); - } - return 0; + return 0; } -std::vector SoapySidekiq::listFrequencies(const int direction, const size_t channel) const { - std::vector names; - names.push_back("RF"); - return names; +std::vector SoapySidekiq::listFrequencies( + const int direction, const size_t channel) const +{ + std::vector names; + names.push_back("RF"); + return names; } -SoapySDR::RangeList SoapySidekiq::getFrequencyRange(const int direction, - const size_t channel, - const std::string &name) const { - SoapySDR::RangeList results; - uint64_t max; - uint64_t min; - - if (direction == SOAPY_SDR_RX && name == "RF") { - if (skiq_read_rx_LO_freq_range(card, &max, &min) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_LO_freq_range (card %d)", card); +SoapySDR::RangeList SoapySidekiq::getFrequencyRange( + const int direction, const size_t channel, const std::string &name) const +{ + SoapySDR::RangeList results; + SoapySDR_log(SOAPY_SDR_TRACE, "getFrequencyRange"); + uint64_t max; + uint64_t min; + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "getFrequencyRange"); + if (direction == SOAPY_SDR_RX && name == "RF") + { + status = skiq_read_rx_LO_freq_range(card, &max, &min); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_LO_freq_range (card %d), status %d", + card, status); + } + results.push_back(SoapySDR::Range(min, max)); } - results.push_back(SoapySDR::Range(min, max)); - } - if (direction == SOAPY_SDR_TX && name == "RF") { - if (skiq_read_tx_LO_freq_range(card, &max, &min) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_tx_LO_freq_range (card %d)", card); + if (direction == SOAPY_SDR_TX && name == "RF") + { + status = skiq_read_tx_LO_freq_range(card, &max, &min); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_tx_LO_freq_range (card %d), status %d", + card, status); + } + results.push_back(SoapySDR::Range(min, max)); } - results.push_back(SoapySDR::Range(min, max)); - } - return results; + return results; } -SoapySDR::ArgInfoList SoapySidekiq::getFrequencyArgsInfo(const int direction, const size_t channel) const { - SoapySDR::ArgInfoList freqArgs; +SoapySDR::ArgInfoList SoapySidekiq::getFrequencyArgsInfo( + const int direction, const size_t channel) const +{ + SoapySDR::ArgInfoList freqArgs; + SoapySDR_log(SOAPY_SDR_TRACE, "getFrequencyArgsInfo"); - // TODO: frequency arguments + // TODO: frequency arguments - return freqArgs; + return freqArgs; } /******************************************************************* * Sample Rate API ******************************************************************/ -void SoapySidekiq::setSampleRate(const int direction, const size_t channel, const double rate) { - if (direction == SOAPY_SDR_RX) { - rx_sample_rate = (uint32_t) rate; - resetBuffer = true; - SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx sample rate: %d", rx_sample_rate); - if (skiq_write_rx_sample_rate_and_bandwidth(card, rx_hdl, rx_sample_rate, rx_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_rx_sample_rate_and_bandwidth (card %d, sample_rate %d, bandwidth %d)", - card, - rx_sample_rate, - rx_bandwidth); - } - } +void SoapySidekiq::setSampleRate(const int direction, const size_t channel, + const double rate) +{ + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "setSampleRate"); + if (direction == SOAPY_SDR_RX) + { + rx_sample_rate = (uint32_t)rate; + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx sample rate: %d", + rx_sample_rate); + status = skiq_write_rx_sample_rate_and_bandwidth( + card, rx_hdl, rx_sample_rate, rx_bandwidth); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_sample_rate_and_bandwidth " + "(card %d, sample_rate %d, bandwidth %d, status %d)", + card, rx_sample_rate, rx_bandwidth, status); + } + } + + if (direction == SOAPY_SDR_TX) + { + tx_sample_rate = (uint32_t)rate; + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting tx sample rate: %d", + tx_sample_rate); + status = skiq_write_tx_sample_rate_and_bandwidth( + card, tx_hdl, tx_sample_rate, tx_bandwidth); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_sample_rate_and_bandwidth " + "(card %d, sample_rate %d, bandwidth %d, status %d)", + card, tx_sample_rate, tx_bandwidth, status); + } + } +} - if (direction == SOAPY_SDR_TX) { - tx_sample_rate = (uint32_t) rate; - resetBuffer = true; - SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting tx sample rate: %d", tx_sample_rate); - if (skiq_write_tx_sample_rate_and_bandwidth(card, tx_hdl, tx_sample_rate, tx_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_tx_sample_rate_and_bandwidth (card %d, sample_rate %d, bandwidth %d)", - card, - tx_sample_rate, - tx_bandwidth); - } - } -} - -double SoapySidekiq::getSampleRate(const int direction, const size_t channel) const { - uint32_t rate; - double actual_rate; - if (direction == SOAPY_SDR_RX) { - if (skiq_read_rx_sample_rate(card, rx_hdl, &rate, &actual_rate) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_sample_rate (card %d)", card); - } - return static_cast(rate); - } - - if (direction == SOAPY_SDR_TX) { - if (skiq_read_tx_sample_rate(card, tx_hdl, &rate, &actual_rate) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_tx_sample_rate (card %d)", card); - } - return static_cast(rate); - } - - return SoapySDR::Device::getSampleRate(direction, channel); -} - -std::vector SoapySidekiq::listSampleRates(const int direction, const size_t channel) const { - std::vector results; - - uint32_t min_sample_rate; - if (skiq_read_min_sample_rate(card, &min_sample_rate) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_min_sample_rate (card %d)", card); - } - uint32_t max_sample_rate; - if (skiq_read_max_sample_rate(card, &max_sample_rate) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_min_sample_rate (card %d)", card); - } - - // iterate through all sample rates - uint32_t sample_rate = min_sample_rate; - while (sample_rate <= max_sample_rate) { - results.push_back(sample_rate); - sample_rate += 250000; - } - - return results; -} - -void SoapySidekiq::setBandwidth(const int direction, const size_t channel, const double bw) { - if (direction == SOAPY_SDR_RX) { - rx_bandwidth = (uint32_t) bw; - if (skiq_write_rx_sample_rate_and_bandwidth(card, rx_hdl, rx_sample_rate, rx_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_rx_sample_rate_and_bandwidth (card %d, sample_rate %d, bandwidth %d)", - card, - rx_sample_rate, - rx_bandwidth); - } - } - - if (direction == SOAPY_SDR_TX) { - tx_bandwidth = (uint32_t) bw; - if (skiq_write_tx_sample_rate_and_bandwidth(card, tx_hdl, tx_sample_rate, tx_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, - "Failure: skiq_write_tx_sample_rate_and_bandwidth (card %d, sample_rate %d, bandwidth %d)", - card, - tx_sample_rate, - tx_bandwidth); - } - } -} - -double SoapySidekiq::getBandwidth(const int direction, const size_t channel) const { - uint32_t rate; - double actual_rate; - uint32_t bandwidth; - uint32_t actual_bandwidth; - if (direction == SOAPY_SDR_RX) { - if (skiq_read_rx_sample_rate_and_bandwidth(card, rx_hdl, &rate, &actual_rate, &bandwidth, &actual_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_rx_sample_rate_and_bandwidth (card %d)", card); - } - } - - if (direction == SOAPY_SDR_TX) { - if (skiq_read_tx_sample_rate_and_bandwidth(card, tx_hdl, &rate, &actual_rate, &bandwidth, &actual_bandwidth) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_read_tx_sample_rate_and_bandwidth (card %d)", card); +double SoapySidekiq::getSampleRate(const int direction, + const size_t channel) const +{ + uint32_t rate; + double actual_rate; + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "getSampleRate"); + if (direction == SOAPY_SDR_RX) + { + status = skiq_read_rx_sample_rate(card, rx_hdl, &rate, &actual_rate); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_sample_rate (card %d), status %d", card, + status); + } + return static_cast(rate); } - } - return bandwidth; + if (direction == SOAPY_SDR_TX) + { + status = skiq_read_tx_sample_rate(card, tx_hdl, &rate, &actual_rate); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_tx_sample_rate (card %d), status %d", card, + status); + } + return static_cast(rate); + } + + return SoapySDR::Device::getSampleRate(direction, channel); +} + +std::vector SoapySidekiq::listSampleRates(const int direction, + const size_t channel) const +{ + std::vector results; + int status = 0; + + SoapySDR_log(SOAPY_SDR_TRACE, "listSampleRates"); + uint32_t min_sample_rate; + status = skiq_read_min_sample_rate(card, &min_sample_rate); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_min_sample_rate (card %d), status %d", + card, status); + } + uint32_t max_sample_rate; + status = skiq_read_max_sample_rate(card, &max_sample_rate); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_min_sample_rate (card %d), status %d", + card, status); + } + + // iterate through all sample rates + uint32_t sample_rate = min_sample_rate; + while (sample_rate <= max_sample_rate) + { + results.push_back(sample_rate); + sample_rate += 250000; + } + + return results; +} + +SoapySDR::RangeList SoapySidekiq::getSampleRateRange(const int direction, + const size_t channel) const +{ + int status = 0; + uint32_t min_sample_rate; + uint32_t max_sample_rate; + SoapySDR::RangeList ranges; + + SoapySDR_log(SOAPY_SDR_TRACE, "getSampleRateRange"); + status = skiq_read_min_sample_rate(card, &min_sample_rate); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_min_sample_rate (card %d), status %d", + card, status); + } + status = skiq_read_max_sample_rate(card, &max_sample_rate); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_min_sample_rate (card %d), status %d", + card, status); + } + ranges.push_back(SoapySDR::Range(min_sample_rate, max_sample_rate)); + + return ranges; +} + +void SoapySidekiq::setBandwidth(const int direction, const size_t channel, + const double bw) +{ + int status = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "setBandwidth"); + if (direction == SOAPY_SDR_RX) + { + rx_bandwidth = (uint32_t)bw; + status = skiq_write_rx_sample_rate_and_bandwidth( + card, rx_hdl, rx_sample_rate, rx_bandwidth); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_sample_rate_and_bandwidth " + "(card %d, sample_rate %d, bandwidth %d, status %d)", + card, rx_sample_rate, rx_bandwidth, status); + } + } + SoapySDR_logf(SOAPY_SDR_DEBUG, "Setting rx bandwidth to %f", bw); + + if (direction == SOAPY_SDR_TX) + { + tx_bandwidth = (uint32_t)bw; + status = skiq_write_tx_sample_rate_and_bandwidth( + card, tx_hdl, tx_sample_rate, tx_bandwidth); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_sample_rate_and_bandwidth " + "(card %d, sample_rate %d, bandwidth %d, status %d)", + card, tx_sample_rate, tx_bandwidth, status); + } + } +} + +double SoapySidekiq::getBandwidth(const int direction, + const size_t channel) const +{ + uint32_t rate; + double actual_rate; + uint32_t bandwidth; + uint32_t actual_bandwidth; + + SoapySDR_log(SOAPY_SDR_TRACE, "getBandwidth"); + if (direction == SOAPY_SDR_RX) + { + if (skiq_read_rx_sample_rate_and_bandwidth(card, rx_hdl, &rate, + &actual_rate, &bandwidth, + &actual_bandwidth) != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_rx_sample_rate_and_bandwidth (card %d)", + card); + } + } + + if (direction == SOAPY_SDR_TX) + { + if (skiq_read_tx_sample_rate_and_bandwidth(card, tx_hdl, &rate, + &actual_rate, &bandwidth, + &actual_bandwidth) != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_read_tx_sample_rate_and_bandwidth (card %d)", + card); + } + } + + return bandwidth; } -std::vector SoapySidekiq::listBandwidths(const int direction, const size_t channel) const { - std::vector bandwidths; - bandwidths.push_back(200000); - bandwidths.push_back(300000); - bandwidths.push_back(600000); - bandwidths.push_back(1536000); - bandwidths.push_back(5000000); - bandwidths.push_back(6000000); - bandwidths.push_back(7000000); - bandwidths.push_back(8000000); - return bandwidths; +std::vector SoapySidekiq::listBandwidths(const int direction, + const size_t channel) const +{ + std::vector bandwidths; + SoapySDR_log(SOAPY_SDR_TRACE, "getBandwidth"); + bandwidths.push_back(200000); + bandwidths.push_back(300000); + bandwidths.push_back(600000); + bandwidths.push_back(1536000); + bandwidths.push_back(5000000); + bandwidths.push_back(6000000); + bandwidths.push_back(7000000); + bandwidths.push_back(8000000); + return bandwidths; } /******************************************************************* * Settings API ******************************************************************/ -SoapySDR::ArgInfoList SoapySidekiq::getSettingInfo(void) const { - SoapySDR::ArgInfoList setArgs; +SoapySDR::ArgInfoList SoapySidekiq::getSettingInfo(void) const +{ + SoapySDR::ArgInfoList setArgs; + SoapySDR_logf(SOAPY_SDR_TRACE, "getSettingInfo"); + + SoapySDR::ArgInfo settingArg; + + settingArg.key = "iq_swap"; + settingArg.value = "false"; + settingArg.name = "I/Q Swap"; + settingArg.description = "I/Q Swap Mode"; + settingArg.type = SoapySDR::ArgInfo::BOOL; + setArgs.push_back(settingArg); + + settingArg.key = "counter"; + settingArg.value = "false"; + settingArg.name = "counter"; + settingArg.description = "RF counter mode"; + settingArg.type = SoapySDR::ArgInfo::BOOL; + setArgs.push_back(settingArg); + + settingArg.key = "log"; + settingArg.value = "false"; + settingArg.name = "log"; + settingArg.description = "Set the Log Level"; + settingArg.type = SoapySDR::ArgInfo::BOOL; + setArgs.push_back(settingArg); + return setArgs; +} - SoapySDR::ArgInfo iqSwapArg; +void SoapySidekiq::writeSetting(const std::string &key, + const std::string &value) +{ + int status = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "writeSetting"); + + if (key == "log") + { + if (value == "trace") + { + SoapySDR::setLogLevel(SOAPY_SDR_TRACE); + SoapySDR_logf(SOAPY_SDR_DEBUG, "Set Log Level to TRACE on card %d", + card); + } + else if (value == "debug") + { + SoapySDR::setLogLevel(SOAPY_SDR_DEBUG); + SoapySDR_logf(SOAPY_SDR_DEBUG, "Set Log Level to DEBUG on card %d", + card); + } + else + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Invalid log level received %s", + value.c_str()); + } + } + else if (key == "iq_swap") + { + iq_swap = ((value == "true") ? true : false); + SoapySDR_logf(SOAPY_SDR_DEBUG, "I/Q swap: %s", + iq_swap ? "true" : "false"); + } + else if (key == "counter") + { + if (value == "true") + { + status = + skiq_write_rx_data_src(card, rx_hdl, skiq_data_src_counter); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure in writing data src to counter status is %d", + status); + } + else + { + SoapySDR_log(SOAPY_SDR_DEBUG, "Set rx src to counter mode "); + counter = true; + } + } + else + { + status = skiq_write_rx_data_src(card, rx_hdl, skiq_data_src_iq); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure in writing data src to iq status is %d", + status); + } + else + { + SoapySDR_log(SOAPY_SDR_DEBUG, "Set rx src to normal mode "); + counter = false; + } + } + } + else + { + SoapySDR_logf(SOAPY_SDR_ERROR, "Invalid key %s ", key.c_str()); + } +} - iqSwapArg.key = "iq_swap"; - iqSwapArg.value = "false"; - iqSwapArg.name = "I/Q Swap"; - iqSwapArg.description = "I/Q Swap Mode"; - iqSwapArg.type = SoapySDR::ArgInfo::BOOL; +std::string SoapySidekiq::readSetting(const std::string &key) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "readSetting"); + if (key == "iq_swap") + { + return iq_swap ? "true" : "false"; + } + else if (key == "counter") + { + return counter ? "true" : "false"; + } + else + { + SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); + } + return ""; +} - setArgs.push_back(iqSwapArg); +/******************************************************************* + * Time API + ******************************************************************/ + +std::vector SoapySidekiq::listTimeSources(void) const +{ + std::vector result; + SoapySDR_logf(SOAPY_SDR_TRACE, "listTimeSources"); + + result.push_back("System"); + result.push_back("RF"); + + return result; +} + +void SoapySidekiq::setTimeSource(const std::string &source) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "setTimeSource, source is %s", source.c_str()); - return setArgs; + timeSource = source; + return; } -void SoapySidekiq::writeSetting(const std::string &key, const std::string &value) { - if (key == "iq_swap") { - iq_swap = ((value == "true") ? true : false); - SoapySDR_logf(SOAPY_SDR_DEBUG, "I/Q swap: %s", iq_swap ? "true" : "false"); - } +std::string SoapySidekiq::getTimeSource(void) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getTimeSource"); + return timeSource; } -std::string SoapySidekiq::readSetting(const std::string &key) const { - if (key == "iq_swap") { - return iq_swap ? "true" : "false"; - } +bool SoapySidekiq::hasHardwareTime(const std::string &) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "hasHardwareTime"); + return true; +} + +long long SoapySidekiq::getHardwareTime(const std::string &) const +{ + //TODO + SoapySDR_logf(SOAPY_SDR_TRACE, "getHardwareTime"); + return 0; +} + +void SoapySidekiq::setHardwareTime(const long long timeNs, const std::string &what) +{ + //TODO + SoapySDR_logf(SOAPY_SDR_TRACE, "setHardwareTime"); + if (what == "CMD") this->setCommandTime(timeNs, what); +} - SoapySDR_logf(SOAPY_SDR_WARNING, "Unknown setting '%s'", key.c_str()); - return ""; +void SoapySidekiq::setCommandTime(const long long, const std::string &) +{ + //TODO + SoapySDR_logf(SOAPY_SDR_TRACE, "setCommandTime"); + return; } diff --git a/SoapySidekiq.hpp b/SoapySidekiq.hpp index 234aba6..b25f5d2 100644 --- a/SoapySidekiq.hpp +++ b/SoapySidekiq.hpp @@ -4,256 +4,293 @@ #include +#include +#include +#include #include +#include #include -#include -#include -#include #include -#include #include #include #include -#define DEFAULT_BUFFER_LENGTH (65536) -#define DEFAULT_NUM_BUFFERS (10) -#define DEFAULT_ELEMS_PER_SAMPLE (2) +#define DEFAULT_NUM_BUFFERS (30) +#define DEFAULT_ELEMS_PER_SAMPLE (2) +#define DEFAULT_TX_BUFFER_LENGTH (8188) +//#define DEFAULT_TX_BUFFER_LENGTH (2044) +#define DEFAULT_SLEEP_US (100) +#define SLEEP_1SEC (1 * 1000000) + +class SoapySidekiq : public SoapySDR::Device +{ + public: + SoapySidekiq(const SoapySDR::Kwargs &args); + + ~SoapySidekiq(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; -class SoapySidekiq : public SoapySDR::Device { - public: - SoapySidekiq(const SoapySDR::Kwargs &args); + /******************************************************************* + * Stream API + ******************************************************************/ - ~SoapySidekiq(void); + std::vector getStreamFormats(const int direction, + const size_t channel) const; - /******************************************************************* - * Identification API - ******************************************************************/ + std::string getNativeStreamFormat(const int direction, const size_t channel, + double &fullScale) const; - std::string getDriverKey(void) const; + SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, + const size_t channel) const; - std::string getHardwareKey(void) const; + SoapySDR::Stream *setupStream( + const int direction, const std::string &format, + const std::vector &channels = std::vector(), + const SoapySDR::Kwargs & args = SoapySDR::Kwargs()); - SoapySDR::Kwargs getHardwareInfo(void) const; + void closeStream(SoapySDR::Stream *stream); - /******************************************************************* - * Channels API - ******************************************************************/ + size_t getStreamMTU(SoapySDR::Stream *stream) const; - size_t getNumChannels(const int) const; + int activateStream(SoapySDR::Stream *stream, const int flags = 0, + const long long timeNs = 0, const size_t numElems = 0); - /******************************************************************* - * Stream API - ******************************************************************/ + int deactivateStream(SoapySDR::Stream *stream, const int flags = 0, + const long long timeNs = 0); - std::vector getStreamFormats(const int direction, const size_t channel) const; + int readStream(SoapySDR::Stream *stream, void *const *buffs, + const size_t numElems, int &flags, long long &timeNs, + const long timeoutUs = 100000); - std::string getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const; + int writeStream(SoapySDR::Stream *stream, const void *const *buffs, + const size_t numElems, int &flags, + const long long timeNs = 0, const long timeoutUs = 100000); - SoapySDR::ArgInfoList getStreamArgsInfo(const int direction, const size_t channel) const; + /******************************************************************* + * Direct buffer access API + ******************************************************************/ - SoapySDR::Stream *setupStream(const int direction, const std::string &format, const std::vector &channels = - std::vector(), const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); + size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); - void closeStream(SoapySDR::Stream *stream); + int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, + const size_t handle, void **buffs); - size_t getStreamMTU(SoapySDR::Stream *stream) const; + int acquireReadBuffer(SoapySDR::Stream *stream, size_t &handle, + const void **buffs, int &flags, long long &timeNs, + const long timeoutUs = 100000); - int activateStream(SoapySDR::Stream *stream, - const int flags = 0, - const long long timeNs = 0, - const size_t numElems = 0); + void releaseReadBuffer(SoapySDR::Stream *stream, const size_t handle); - int deactivateStream(SoapySDR::Stream *stream, const int flags = 0, const long long timeNs = 0); + int acquireWriteBuffer(SoapySDR::Stream *stream, size_t &handle, + void **buffs, const long timeoutUs = 100000); - int readStream(SoapySDR::Stream *stream, - void *const *buffs, - const size_t numElems, - int &flags, - long long &timeNs, - const long timeoutUs = 100000); + void releaseWriteBuffer(SoapySDR::Stream *stream, const size_t handle, + const size_t numElems, int &flags, + const long long timeNs = 0); - int writeStream(SoapySDR::Stream *stream, - const void *const *buffs, - const size_t numElems, - int &flags, - const long long timeNs = 0, - const long timeoutUs = 100000); + /******************************************************************* + * Antenna API + ******************************************************************/ - /******************************************************************* - * Direct buffer access API - ******************************************************************/ + std::vector listAntennas(const int direction, + const size_t channel) const; - size_t getNumDirectAccessBuffers(SoapySDR::Stream *stream); + void setAntenna(const int direction, const size_t channel, + const std::string &name); - int getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs); + std::string getAntenna(const int direction, const size_t channel) const; - int acquireReadBuffer(SoapySDR::Stream *stream, - size_t &handle, - const void **buffs, - int &flags, - long long &timeNs, - const long timeoutUs = 100000); + /******************************************************************* + * Frontend corrections API + ******************************************************************/ - void releaseReadBuffer(SoapySDR::Stream *stream, const size_t handle); + bool hasDCOffsetMode(const int direction, const size_t channel) const; - int acquireWriteBuffer(SoapySDR::Stream *stream, - size_t &handle, - void **buffs, - const long timeoutUs = 100000); + void setDCOffsetMode(const int direction, const size_t channel, + const bool automatic); - void releaseWriteBuffer(SoapySDR::Stream *stream, - const size_t handle, - const size_t numElems, - int &flags, - const long long timeNs = 0); + bool getDCOffsetMode(const int direction, const size_t channel) const; - /******************************************************************* - * Antenna API - ******************************************************************/ + /******************************************************************* + * Gain API + ******************************************************************/ - std::vector listAntennas(const int direction, const size_t channel) const; + std::vector listGains(const int direction, + const size_t channel) const; - void setAntenna(const int direction, const size_t channel, const std::string &name); + bool hasGainMode(const int direction, const size_t channel) const; - std::string getAntenna(const int direction, const size_t channel) const; + void setGainMode(const int direction, const size_t channel, + const bool automatic); - /******************************************************************* - * Frontend corrections API - ******************************************************************/ + bool getGainMode(const int direction, const size_t channel) const; - bool hasDCOffsetMode(const int direction, const size_t channel) const; + void setGain(const int direction, const size_t channel, const double value); - void setDCOffsetMode(const int direction, const size_t channel, const bool automatic); + double getGain(const int direction, const size_t channel) const; - bool getDCOffsetMode(const int direction, const size_t channel) const; + SoapySDR::Range getGainRange(const int direction, + const size_t channel) const; - /******************************************************************* - * Gain API - ******************************************************************/ + /******************************************************************* + * Frequency API + ******************************************************************/ - std::vector listGains(const int direction, const size_t channel) const; + void setFrequency(const int direction, const size_t channel, + const std::string &name, const double frequency, + const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); - bool hasGainMode(const int direction, const size_t channel) const; + double getFrequency(const int direction, const size_t channel, + const std::string &name) const; - void setGainMode(const int direction, const size_t channel, const bool automatic); + std::vector listFrequencies(const int direction, + const size_t channel) const; - bool getGainMode(const int direction, const size_t channel) const; + SoapySDR::RangeList getFrequencyRange(const int direction, + const size_t channel, + const std::string &name) const; - void setGain(const int direction, const size_t channel, const double value); + SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, + const size_t channel) const; - double getGain(const int direction, const size_t channel) const; + /******************************************************************* + * Sample Rate API + ******************************************************************/ - SoapySDR::Range getGainRange(const int direction, const size_t channel, const std::string &name) const; + void setSampleRate(const int direction, const size_t channel, + const double rate); - /******************************************************************* - * Frequency API - ******************************************************************/ + double getSampleRate(const int direction, const size_t channel) const; - void setFrequency(const int direction, - const size_t channel, - const std::string &name, - const double frequency, - const SoapySDR::Kwargs &args = SoapySDR::Kwargs()); + SoapySDR::RangeList getSampleRateRange(const int direction, + const size_t channel) const; - double getFrequency(const int direction, const size_t channel, const std::string &name) const; + std::vector listSampleRates(const int direction, + const size_t channel) const; - std::vector listFrequencies(const int direction, const size_t channel) const; + void setBandwidth(const int direction, const size_t channel, + const double bw); - SoapySDR::RangeList getFrequencyRange(const int direction, const size_t channel, const std::string &name) const; + double getBandwidth(const int direction, const size_t channel) const; - SoapySDR::ArgInfoList getFrequencyArgsInfo(const int direction, const size_t channel) const; + std::vector listBandwidths(const int direction, + const size_t channel) const; - /******************************************************************* - * Sample Rate API - ******************************************************************/ + /******************************************************************* + * Attenuation API + ******************************************************************/ - void setSampleRate(const int direction, const size_t channel, const double rate); + // TODO - double getSampleRate(const int direction, const size_t channel) const; + /******************************************************************* + * Sensor API + ******************************************************************/ + std::vector listSensors(void) const; + SoapySDR::ArgInfo getSensorInfo(const std::string &key) const; + std::string readSensor(const std::string &key) const; + std::vector listSensors(const int direction, + const size_t channel) const; + std::string readSensor(const int direction, const size_t channel, + const std::string &key) const; - std::vector listSampleRates(const int direction, const size_t channel) const; + /******************************************************************* + * Settings API + ******************************************************************/ - void setBandwidth(const int direction, const size_t channel, const double bw); + SoapySDR::ArgInfoList getSettingInfo(void) const; - double getBandwidth(const int direction, const size_t channel) const; + void writeSetting(const std::string &key, const std::string &value); - std::vector listBandwidths(const int direction, const size_t channel) const; + std::string readSetting(const std::string &key) const; - /******************************************************************* - * Attenuation API - ******************************************************************/ + /******************************************************************* + * Time API + ******************************************************************/ - // TODO + std::vector listTimeSources(void) const; - /******************************************************************* - * Sensor API - ******************************************************************/ - std::vector listSensors(void) const; - SoapySDR::ArgInfo getSensorInfo(const std::string &key) const; - std::string readSensor(const std::string &key) const; - std::vector listSensors(const int direction, const size_t channel) const; - std::string readSensor(const int direction, const size_t channel, const std::string &key) const; + void setTimeSource(const std::string &source); - /******************************************************************* - * Settings API - ******************************************************************/ + std::string getTimeSource(void) const; - SoapySDR::ArgInfoList getSettingInfo(void) const; + bool hasHardwareTime(const std::string &) const; - void writeSetting(const std::string &key, const std::string &value); + long long getHardwareTime(const std::string &) const; - std::string readSetting(const std::string &key) const; + void setHardwareTime(const long long timeNs, const std::string &what); - private: + void setCommandTime(const long long, const std::string &); - SoapySDR::Stream* const TX_STREAM = (SoapySDR::Stream*) 0x1; - SoapySDR::Stream* const RX_STREAM = (SoapySDR::Stream*) 0x2; + private: + SoapySDR::Stream *const TX_STREAM = (SoapySDR::Stream *)0x1; + SoapySDR::Stream *const RX_STREAM = (SoapySDR::Stream *)0x2; - // sidekiq card - uint8_t card; + // sidekiq card + uint8_t card{}; + std::basic_string serial{}; + std::basic_string timeSource{}; + uint32_t resolution{}; + double max_value{}; - // sidekiq hdl - skiq_rx_hdl_t rx_hdl; - skiq_tx_hdl_t tx_hdl; + // sidekiq hdl - // rx - uint64_t rx_center_frequency; - uint32_t rx_sample_rate, rx_bandwidth; + bool useShort{}; + uint32_t debug_ctr{}; - // tx - uint64_t tx_center_frequency; - uint32_t tx_sample_rate, tx_bandwidth; + // rx + uint8_t num_rx_channels{}; + skiq_rx_hdl_t rx_hdl{}; + uint64_t rx_center_frequency{}; + uint32_t rx_sample_rate{}, rx_bandwidth{}; + uint32_t rx_block_size_in_words{}; + uint32_t rx_block_size_in_bytes{}; + uint32_t rx_payload_size_in_bytes{}; + uint32_t rx_payload_size_in_words{}; - // setting - bool iq_swap; + // tx + uint8_t num_tx_channels{}; + skiq_tx_hdl_t tx_hdl; + uint64_t tx_center_frequency{}; + uint32_t tx_sample_rate{}, tx_bandwidth{}; + uint32_t tx_underruns{}; - // buffer - size_t numBuffers; - unsigned int bufferElems = DEFAULT_BUFFER_LENGTH; - const int elementsPerSample = DEFAULT_ELEMS_PER_SAMPLE; - size_t bufferLength; - std::atomic_uint shortsPerWord; - std::atomic_bool useShort; + // setting + bool iq_swap{}; + bool counter{}; + bool log; - public: - // receive thread - std::thread _rx_receive_thread; - void rx_receive_operation(void); + // RX buffer + skiq_rx_block_t *p_rx_block[DEFAULT_NUM_BUFFERS]; + uint32_t rxReadIndex{}; + uint32_t rxWriteIndex{}; + uint32_t p_rx_block_index{}; - std::mutex _buf_mutex; - std::condition_variable _buf_cond; + // TX buffer + skiq_tx_block_t *p_tx_block[DEFAULT_NUM_BUFFERS]; + uint32_t currTXBuffIndex{}; + uint32_t p_tx_block_index{}; - std::vector > _buffs; - size_t _buf_head; - size_t _buf_tail; - std::atomic _buf_count; - int16_t *_currentBuff; - std::atomic _overflowEvent; - size_t _currentHandle; - size_t bufferedElems; - std::atomic resetBuffer; + public: + // receive thread + std::thread _rx_receive_thread; + void rx_receive_operation(void); - static std::vector sidekiq_devices; - static bool rx_running; + static std::vector sidekiq_devices; + static bool rx_running; }; diff --git a/Streaming.cpp b/Streaming.cpp index 5a4a1ac..f2c4989 100644 --- a/Streaming.cpp +++ b/Streaming.cpp @@ -2,489 +2,899 @@ #include "SoapySidekiq.hpp" #include -#include // memcpy +#include // memcpy +#include #include - -std::vector SoapySidekiq::getStreamFormats(const int direction, const size_t channel) const { - std::vector formats; - formats.push_back(SOAPY_SDR_CS16); - formats.push_back(SOAPY_SDR_CF32); - return formats; +#include + +std::vector SoapySidekiq::getStreamFormats( + const int direction, const size_t channel) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getStreamFormats"); + std::vector formats; + formats.push_back(SOAPY_SDR_CS16); + formats.push_back(SOAPY_SDR_CF32); + return formats; } -std::string SoapySidekiq::getNativeStreamFormat(const int direction, const size_t channel, double &fullScale) const { - fullScale = 32767; - return "CS16"; +std::string SoapySidekiq::getNativeStreamFormat(const int direction, + const size_t channel, + double & fullScale) const +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getNativeStreamFormat"); + + fullScale = this->max_value; + + return "CS16"; } -SoapySDR::ArgInfoList SoapySidekiq::getStreamArgsInfo(const int direction, const size_t channel) const { - SoapySDR::ArgInfoList streamArgs; +SoapySDR::ArgInfoList SoapySidekiq::getStreamArgsInfo( + const int direction, const size_t channel) const +{ + SoapySDR::ArgInfoList streamArgs; - SoapySDR::ArgInfo bufflenArg; - bufflenArg.key = "bufflen"; - bufflenArg.value = std::to_string(DEFAULT_BUFFER_LENGTH); - bufflenArg.name = "Buffer Sample Count"; - bufflenArg.description = "Number of IQ samples per buffer."; - bufflenArg.units = "samples"; - bufflenArg.type = SoapySDR::ArgInfo::INT; + SoapySDR::ArgInfo bufflenArg; + bufflenArg.key = "bufflen"; - streamArgs.push_back(bufflenArg); + SoapySDR_logf(SOAPY_SDR_TRACE, "getStreamArgsInfo"); - SoapySDR::ArgInfo buffersArg; - buffersArg.key = "buffers"; - buffersArg.value = std::to_string(DEFAULT_NUM_BUFFERS); - buffersArg.name = "Ring Buffers"; - buffersArg.description = "Number of buffers in the ring."; - buffersArg.units = "buffers"; - buffersArg.type = SoapySDR::ArgInfo::INT; + if (direction == SOAPY_SDR_RX) + { + bufflenArg.value = std::to_string(rx_block_size_in_words); + } + else + { + bufflenArg.value = std::to_string(DEFAULT_TX_BUFFER_LENGTH); + } + + bufflenArg.name = "Buffer Sample Count"; + bufflenArg.description = "Number of IQ samples per buffer."; + bufflenArg.units = "samples"; + bufflenArg.type = SoapySDR::ArgInfo::INT; + + streamArgs.push_back(bufflenArg); - streamArgs.push_back(buffersArg); + SoapySDR::ArgInfo buffersArg; + buffersArg.key = "buffers"; + buffersArg.value = std::to_string(DEFAULT_NUM_BUFFERS); + buffersArg.name = "Ring Buffers"; + buffersArg.description = "Number of buffers in the ring."; + buffersArg.units = "buffers"; + buffersArg.type = SoapySDR::ArgInfo::INT; - return streamArgs; + streamArgs.push_back(buffersArg); + + return streamArgs; } /******************************************************************* * Sidekiq receive thread ******************************************************************/ -void SoapySidekiq::rx_receive_operation(void) { - SoapySDR_log(SOAPY_SDR_INFO, "Starting RX Sidekiq Thread"); - - /* set rx source as iq data */ - if (skiq_write_rx_data_src(card, rx_hdl, skiq_data_src_iq) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_rx_data_src (card %d)", card); - } +void SoapySidekiq::rx_receive_operation(void) +{ + int status = 0; - /* set a modest rx timeout */ - if (skiq_set_rx_transfer_timeout(card, 100000) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_set_rx_transfer_timeout (card %d)", card); - } + SoapySDR_log(SOAPY_SDR_INFO, "Starting RX Sidekiq Thread"); - /* start rx streaming */ - if (skiq_start_rx_streaming(card, rx_hdl) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_start_rx_streaming (card %d)", card); - } + // status = skiq_write_rx_data_src(card, rx_hdl, skiq_data_src_counter); - // skiq receive params - skiq_rx_block_t *p_rx_block; - uint32_t len; - - // metadata - uint64_t overload = 0; - - // loop until stream is deactivated - while (rx_running) { - - // check for overflow - if (_buf_count == numBuffers || overload) { - SoapySDR_log(SOAPY_SDR_WARNING, "Detected overflow Event in RX Sidekiq Thread"); - _overflowEvent = true; + /* set rx source as iq data */ + status = skiq_write_iq_order_mode(card, skiq_iq_order_qi); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_write_rx_data_src (card %d) status %d", + card, status); + throw std::runtime_error(" skiq_write_rx_data_src error"); } - /* blocking skiq_receive */ - if (skiq_receive(card, &rx_hdl, &p_rx_block, &len) == skiq_rx_status_success) { - // metadata - overload = p_rx_block->overload; + skiq_iq_order_t iq_order; + status = skiq_read_iq_order_mode(card, &iq_order); - // number of i and q samples - uint32_t num_samples = (len - SKIQ_RX_HEADER_SIZE_IN_BYTES) / sizeof(int16_t); + /* set a modest rx timeout */ + status = skiq_set_rx_transfer_timeout(card, 100000); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_set_rx_transfer_timeout (card %d) status %d", card, + status); + throw std::runtime_error(" skiq_set_rx_transfer_timeout ( error"); + } - // buffer space required - uint32_t space_req = num_samples * elementsPerSample * shortsPerWord; + /* start rx streaming */ + status = skiq_start_rx_streaming(card, rx_hdl); + if (skiq_start_rx_streaming(card, rx_hdl) != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_start_rx_streaming (card %d) status %d", + card, status); + throw std::runtime_error(" skiq_start_rx_streaming error"); + } - // buf mutex - { - std::lock_guard lock(_buf_mutex); - // check if we need to move on to next buffer in ring - if ((_buffs[_buf_tail].size() + space_req) >= (bufferLength)) { - SoapySDR_logf(SOAPY_SDR_TRACE, "Rotating Buffer Ring %d", _buf_count.load()); + // skiq receive params + skiq_rx_block_t *tmp_p_rx_block; + uint32_t len; - // increment the tail pointer and buffer count - _buf_tail = (_buf_tail + 1) % numBuffers; - _buf_count++; + // metadata + uint64_t overload = 0; + skiq_rx_hdl_t rcvd_hdl; - // notify readStream() - _buf_cond.notify_one(); + // loop until stream is deactivated + while (rx_running) + { + // check for overflow + if (rxReadIndex == ((rxWriteIndex + 1) % DEFAULT_NUM_BUFFERS) || + overload) + { + SoapySDR_log(SOAPY_SDR_WARNING, + "Detected overflow Event in RX Sidekiq Thread"); + SoapySDR_logf(SOAPY_SDR_DEBUG, + "rxReadIndex %d, rxWriteIndex %d, overload %d\n", + rxReadIndex, rxWriteIndex, overload); + rxWriteIndex = rxReadIndex; + p_rx_block_index = 0; } - - // get current fill buffer - auto &buff = _buffs[_buf_tail]; - buff.resize(buff.size() + space_req); - - // copy into the buffer queue - unsigned int i = 0; - - if (useShort) { // int16_t - int16_t *dptr = buff.data(); - dptr += (buff.size() - space_req); - for (i = 0; i < (num_samples / elementsPerSample); i++) { - if (!iq_swap) { - *dptr++ = p_rx_block->data[i * 2 + 1]; // I - *dptr++ = p_rx_block->data[i * 2]; // Q - } else { - *dptr++ = p_rx_block->data[i * 2]; // Q - *dptr++ = p_rx_block->data[i * 2 + 1]; // I + else + { + /* + * put the block into a ring buffer so readStream can read it out + * at a different pace */ + status = skiq_receive(card, &rcvd_hdl, &tmp_p_rx_block, &len); + if (status == skiq_rx_status_success) + { + if (rcvd_hdl == rx_hdl) + { + if (len != rx_block_size_in_bytes) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "received length %d is not the correct " + "block size %d\n", + len, rx_block_size_in_bytes); + throw std::runtime_error("skiq_receive length error"); + } + + // get overload out of metadata + overload = tmp_p_rx_block->overload; + + int num_words_read = (len / 4); + + // copy the data out of the rx_buffer and into the ring + // buffers copy the header in also + memcpy(p_rx_block[rxWriteIndex], (void *)tmp_p_rx_block, + (num_words_read * sizeof(uint32_t))); +#ifdef DEBUG + uint8_t *temp_ptr = (uint8_t *)p_rx_block[rxWriteIndex]; + + for (int i = 0; i < num_words_read; i++) + { + if ((rxWriteIndex == 0) && (i < 10 || i > 1000)) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, + "R %d, 0x%02X%02X 0x%02X%02X ", i, + *temp_ptr++, *temp_ptr++, *temp_ptr++, + *temp_ptr++); + } + } + + printf(" done \n"); +#endif + + rxWriteIndex = (rxWriteIndex + 1) % DEFAULT_NUM_BUFFERS; + } } - } - } else { // float - float *dptr = (float *) buff.data(); - dptr += ((buff.size() - space_req) / shortsPerWord); - for (i = 0; i < (num_samples / elementsPerSample); i++) { - if (!iq_swap) { - *dptr++ = (float) p_rx_block->data[i * 2 + 1] / 32768.0f; // I - *dptr++ = (float) p_rx_block->data[i * 2] / 32768.0f; // Q - } else { - *dptr++ = (float) p_rx_block->data[i * 2] / 32768.0f; // Q - *dptr++ = (float) p_rx_block->data[i * 2 + 1] / 32768.0f; // I + else + { + if (status != -1) + { + SoapySDR_logf(SOAPY_SDR_FATAL, + "Failure: skiq_receive (card %d) status %d", + card, status); + throw std::runtime_error("skiq_receive error"); + } } - } } - } - } else { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_receive (card %d)", card); } - } - SoapySDR_log(SOAPY_SDR_INFO, "Exiting RX Sidekiq Thread"); + SoapySDR_log(SOAPY_SDR_INFO, "Exiting RX Sidekiq Thread"); } /******************************************************************* * Stream API ******************************************************************/ -SoapySDR::Stream *SoapySidekiq::setupStream(const int direction, +SoapySDR::Stream *SoapySidekiq::setupStream(const int direction, const std::string &format, const std::vector &channels, - const SoapySDR::Kwargs &args) { - // check the channel configuration - if (channels.size() > 1 || (channels.size() > 0 && channels.at(0) != 0)) { - throw std::runtime_error("setupStream invalid channel selection"); - } - - if (direction == SOAPY_SDR_RX) { - bufferElems = DEFAULT_BUFFER_LENGTH; - if (args.count("bufflen") != 0) { - try { - int bufferLength_in = std::stoi(args.at("bufflen")); - if (bufferLength_in > 0) { - bufferElems = bufferLength_in; - } - } - catch (const std::invalid_argument &) {} - } - SoapySDR_logf(SOAPY_SDR_DEBUG, "Sidekiq Using rx sample buffer length %d", bufferElems); - - // check the format - if (format == "CS16") { - useShort = true; - shortsPerWord = 1; - bufferLength = bufferElems * elementsPerSample * shortsPerWord; - SoapySDR_log(SOAPY_SDR_INFO, "Using format CS16."); - } else if (format == "CF32") { - useShort = false; - shortsPerWord = sizeof(float) / sizeof(short); - bufferLength = - bufferElems * elementsPerSample * shortsPerWord; // allocate enough space for floats instead of shorts - SoapySDR_log(SOAPY_SDR_INFO, "Using format CF32."); - } else { - throw std::runtime_error( - "setupStream invalid format '" + format - + "' -- Only CS16 or CF32 is supported by SoapySidekiq module."); - } - - numBuffers = DEFAULT_NUM_BUFFERS; - if (args.count("buffers") != 0) { - try { - int numBuffers_in = std::stoi(args.at("buffers")); - if (numBuffers_in > 0) { - numBuffers = numBuffers_in; - } - } - catch (const std::invalid_argument &) {} - } - SoapySDR_logf(SOAPY_SDR_DEBUG, "Sidekiq Using %d rx buffers", numBuffers); - - // buff mutext - { - std::lock_guard lock(_buf_mutex); - - // 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); - for (auto &buff : _buffs) buff.clear(); - } - return RX_STREAM; - } else if (direction == SOAPY_SDR_TX) { - return TX_STREAM; - } else { - throw std::runtime_error("Invalid direction"); - } -} + const SoapySDR::Kwargs & args) +{ -void SoapySidekiq::closeStream(SoapySDR::Stream *stream) { + int status = 0; + skiq_rx_stream_mode_t stream_mode = skiq_rx_stream_mode_balanced; + SoapySDR_logf(SOAPY_SDR_TRACE, "setupStream"); + + rx_block_size_in_words = 0; + + // check the channel configuration + if (channels.size() > 1 || (channels.size() > 0 && channels.at(0) != 0)) + { + throw std::runtime_error("setupStream invalid channel selection"); + } + + if (direction == SOAPY_SDR_RX) + { + status = skiq_write_rx_stream_mode(card, stream_mode); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Error: unable to set RX stream mode with status %d\n", status); + } + status = skiq_read_rx_block_size(card, stream_mode); + if (status < 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + " failed to read RX block size status %d, with status %d\n", + rx_block_size_in_bytes, status); + throw std::runtime_error("skiq_read_rx_block_size"); + } + rx_block_size_in_bytes = status; + rx_payload_size_in_bytes = status - SKIQ_RX_HEADER_SIZE_IN_BYTES; + rx_payload_size_in_words = rx_payload_size_in_bytes / 4; + + // allocate the ring buffers + for (int i = 0; i < DEFAULT_NUM_BUFFERS; i++) + { + p_rx_block[i] = (skiq_rx_block_t *)malloc(rx_block_size_in_bytes); + if (p_rx_block[i] == NULL) + { + SoapySDR_log(SOAPY_SDR_ERROR, + " malloc failed to allocate memory "); + throw std::runtime_error("malloc failed to allocate memory"); + } + + memset(p_rx_block[i], 0, rx_block_size_in_bytes); + } + rxWriteIndex = 0; + rxReadIndex = 0; + + rx_block_size_in_words = rx_block_size_in_bytes / 4; + + if (format == "CS16") + { + useShort = true; + SoapySDR_log(SOAPY_SDR_INFO, "Using format CS16\n"); + } + else if (format == "CF32") + { + useShort = false; + SoapySDR_log(SOAPY_SDR_INFO, "Using format CF32\n"); + } + else + { + throw std::runtime_error( + "setupStream invalid format '" + format + + "' -- Only CS16 or CF32 is supported by SoapySidekiq module."); + } + + return RX_STREAM; + } + else if (direction == SOAPY_SDR_TX) + { + // check the channel configuration + if (channels.size() > 1 || (channels.size() > 0 && channels.at(0) != 0)) + { + throw std::runtime_error("setupStream invalid channel selection"); + } - this->deactivateStream(stream, 0, 0); + // check the format only support CS16 for now + if (format == "CS16") + { + SoapySDR_log(SOAPY_SDR_INFO, "Using format CS16\n"); + } + else + { + throw std::runtime_error( + "setupStream invalid format '" + format + + "' -- Only CS16 or CF32 is supported by SoapySidekiq module."); + } - if (stream == RX_STREAM) { - _buffs.clear(); - } + // Allocate buffers + for (int i = 0; i < DEFAULT_NUM_BUFFERS; i++) + { + p_tx_block[i] = skiq_tx_block_allocate(DEFAULT_TX_BUFFER_LENGTH); + } + currTXBuffIndex = 0; + return TX_STREAM; + } + else + { + throw std::runtime_error("Invalid direction"); + } } -size_t SoapySidekiq::getStreamMTU(SoapySDR::Stream *stream) const { - return bufferLength; +void SoapySidekiq::closeStream(SoapySDR::Stream *stream) +{ + + SoapySDR_logf(SOAPY_SDR_TRACE, "closeStream"); + this->deactivateStream(stream, 0, 0); + + if (stream == RX_STREAM) + { + for (int i = 0; i < DEFAULT_NUM_BUFFERS; i++) + { + free(p_rx_block[i]); + } + } + else if (stream == TX_STREAM) + { + for (int i = 0; i < DEFAULT_NUM_BUFFERS; i++) + { + skiq_tx_block_free(p_tx_block[i]); + } + } } -int SoapySidekiq::activateStream(SoapySDR::Stream *stream, - const int flags, - const long long timeNs, - const size_t numElems) { - if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; +size_t SoapySidekiq::getStreamMTU(SoapySDR::Stream *stream) const +{ - if (stream == RX_STREAM) { - resetBuffer = true; - bufferedElems = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "getStremMTU"); - // start the receive thread - if (!_rx_receive_thread.joinable()) { - SoapySDR_logf(SOAPY_SDR_DEBUG, "Start RX"); - _rx_receive_thread = std::thread(&SoapySidekiq::rx_receive_operation, this); + if (stream == RX_STREAM) + { + // return (DEFAULT_NUM_BUFFERS * (rx_block_size_in_words - + // SKIQ_RX_HEADER_SIZE_IN_WORDS)); + return (2 * (rx_block_size_in_words - SKIQ_RX_HEADER_SIZE_IN_WORDS)); } - } else if (stream == TX_STREAM) { - SoapySDR_logf(SOAPY_SDR_DEBUG, "Start TX"); - /* start tx streaming */ - if (skiq_start_tx_streaming(card, tx_hdl) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_start_tx_streaming (card %d)", card); + else if (stream == TX_STREAM) + { + return DEFAULT_TX_BUFFER_LENGTH; + } + else + { + return SOAPY_SDR_NOT_SUPPORTED; } - } - return 0; + return 0; } -int SoapySidekiq::deactivateStream(SoapySDR::Stream *stream, const int flags, const long long timeNs) { - if (flags != 0) return SOAPY_SDR_NOT_SUPPORTED; - if (stream == RX_STREAM) { - // stop receive thread - if (_rx_receive_thread.joinable()) { - rx_running = false; - _rx_receive_thread.join(); +int SoapySidekiq::activateStream(SoapySDR::Stream *stream, const int flags, + const long long timeNs, const size_t numElems) +{ + + int status = 0; + + SoapySDR_logf(SOAPY_SDR_TRACE, "activateStream"); + if (flags != 0) + return SOAPY_SDR_NOT_SUPPORTED; + + if (stream == RX_STREAM) + { + p_rx_block_index = 0; + + // start the receive thread + if (!_rx_receive_thread.joinable()) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Start RX"); + rx_running = true; + _rx_receive_thread = + std::thread(&SoapySidekiq::rx_receive_operation, this); + } } + else if (stream == TX_STREAM) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "Start TX"); + p_tx_block_index = 0; + tx_underruns = 0; + + // tx block size + status = + skiq_write_tx_block_size(card, tx_hdl, DEFAULT_TX_BUFFER_LENGTH); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_block_size (card %d) status %d", card, + status); + throw std::runtime_error("skiq_write_tx_block_size error"); + } - /* stop rx streaming */ - if (skiq_stop_rx_streaming(card, rx_hdl) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_stop_rx_streaming (card %d)", card); + // tx data flow mode + status = skiq_write_tx_data_flow_mode(card, tx_hdl, + skiq_tx_immediate_data_flow_mode); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_write_tx_data_flow_mode (card %d) status %d", + card, status); + throw std::runtime_error("skiq_write_tx_data_flow_mode error"); + } + + // transfer mode (sync for now) + status = skiq_write_tx_transfer_mode(card, tx_hdl, + skiq_tx_transfer_mode_sync); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_tx_transfer_mode (card %d) status %d", + card, status); + throw std::runtime_error("skiq_tx_transfer_mode error"); + } + + /* start tx streaming */ + status = skiq_start_tx_streaming(card, tx_hdl); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_start_tx_streaming (card %d) status %d", card, + status); + throw std::runtime_error("skiq_start_tx_streaming error"); + } + } + + return 0; +} + +int SoapySidekiq::deactivateStream(SoapySDR::Stream *stream, const int flags, + const long long timeNs) +{ + int status = 0; + SoapySDR_logf(SOAPY_SDR_TRACE, "deactivateStream"); + + if (flags != 0) + return SOAPY_SDR_NOT_SUPPORTED; + + if (stream == RX_STREAM && rx_running == true) + { + // stop receive thread + rx_running = false; + + /* stop rx streaming */ + status = skiq_stop_rx_streaming(card, rx_hdl); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_stop_rx_streaming (card %d) handle " + "%d, status %d", + card, rx_hdl, status); + } + + /* wait till the rx thread is done */ + if (_rx_receive_thread.joinable()) + { + _rx_receive_thread.join(); + } } - } else if (stream == TX_STREAM) { - /* stop tx streaming */ - if (skiq_stop_tx_streaming(card, tx_hdl) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_stop_tx_streaming (card %d)", card); + else if (stream == TX_STREAM) + { + /* stop tx streaming */ + status = skiq_stop_tx_streaming(card, tx_hdl); + if (status != 0) + { + SoapySDR_logf( + SOAPY_SDR_ERROR, + "Failure: skiq_stop_tx_streaming (card %d), status %d", card, + status); + } } - } - return 0; + return 0; } -int SoapySidekiq::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; - } - - // drop remainder buffer on reset - if (resetBuffer && bufferedElems != 0) { - bufferedElems = 0; - this->releaseReadBuffer(stream, _currentHandle); - } - - // 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); - - // copy into user's buff0 - if (useShort) { - std::memcpy(buff0, _currentBuff, returnedElems * elementsPerSample * sizeof(int16_t)); - } else { - std::memcpy(buff0, (float *) _currentBuff, returnedElems * 2 * sizeof(float)); - } - - // bump variables for next call into readStream - bufferedElems -= returnedElems; - - // scope lock here to update _currentBuff position - { - std::lock_guard lock(_buf_mutex); - _currentBuff += returnedElems * elementsPerSample * shortsPerWord; - } - - // return number of elements written to buff0 - if (bufferedElems != 0) - flags |= SOAPY_SDR_MORE_FRAGMENTS; - else - this->releaseReadBuffer(stream, _currentHandle); - return returnedElems; +int SoapySidekiq::readStream(SoapySDR::Stream *stream, void *const *buffs, + const size_t numElems, int &flags, + long long &timeNs, const long timeoutUs) +{ + long waitTime = timeoutUs; + + if (stream != RX_STREAM) + { + return SOAPY_SDR_NOT_SUPPORTED; + } + + // if the user didn't give a waittime then wait a LONG time + if (waitTime == 0) + { + waitTime = SLEEP_1SEC; + } + + // see if we have any receive buffers to give + while ((rxReadIndex == rxWriteIndex) && (waitTime > 0)) + { + // wait + usleep(DEFAULT_SLEEP_US); + waitTime -= DEFAULT_SLEEP_US; + p_rx_block_index = 0; + } + + if (waitTime <= 0) + { + SoapySDR_log(SOAPY_SDR_DEBUG, "readStream timed out"); + return SOAPY_SDR_TIMEOUT; + } + + // The numElems may be smaller or bigger than our receive buffer length + // if smaller we will need to send multiple responses for one receive buffer + // if bigger we will have to wait for multiple receive buffers to be + // received before we return + size_t numElemsLeft = numElems; + char * buff_ptr = (char *)buffs[0]; + + // we need to handle the case where we left in the middle of a ring buffer + // the p_rx_block_index is != 0 if that is the case + uint32_t words_left_in_block = + rx_payload_size_in_words - (p_rx_block_index / 4); + char *ringbuffer_ptr = + (char *)(((char *)p_rx_block[rxReadIndex]->data) + p_rx_block_index); + + // determine if we have more words in the ring buffer block than we need (or + // equal) + while (numElemsLeft >= words_left_in_block) + { +//#define debug +#ifdef debug + char *last_buff_ptr = buff_ptr; + char *last_ringbuffer_ptr = ringbuffer_ptr; + SoapySDR_logf( + SOAPY_SDR_DEBUG, + "1 p_rx_block_index %d, numElemsLeft %d, words_left_in_block %d", + p_rx_block_index, numElemsLeft, words_left_in_block); + SoapySDR_logf(SOAPY_SDR_DEBUG, "rxReadIndex %d, rxWriteIndex %d", + rxReadIndex, rxWriteIndex); + SoapySDR_logf(SOAPY_SDR_DEBUG, "buff_ptr %p, ringbuffer_ptr %p", + buff_ptr, ringbuffer_ptr); + SoapySDR_logf(SOAPY_SDR_DEBUG, + "size of float %d buff_ptr delta %ld, ringbuffer_ptr %ld", + sizeof(float), buff_ptr - last_buff_ptr, + ringbuffer_ptr - last_ringbuffer_ptr); + + last_ringbuffer_ptr = ringbuffer_ptr; + last_buff_ptr = buff_ptr; + + // SoapySDR_logf(SOAPY_SDR_DEBUG, "ringbuffer %p, ringbuffer_ptr %p", + // p_rx_block[rxReadIndex]->data , ringbuffer_ptr); + // SoapySDR_logf(SOAPY_SDR_DEBUG, "rx_payload_size_in_words, %d", + // rx_payload_size_in_words); + + if (debug_ctr % 1000 == 0) + { + printf(" %u\n", debug_ctr); + } + debug_ctr++; +#endif + + uint32_t bytes_left_in_block = words_left_in_block * 4; + + // copy in the amount of data we have in the ring block + if (useShort == true) + { // CS16 + memcpy(buff_ptr, ringbuffer_ptr, bytes_left_in_block); + } + else + { // float + float * dbuff_ptr = (float *)buff_ptr; + int16_t *source = (int16_t *)ringbuffer_ptr; + + int short_ctr = 0; + for (uint32_t i = 0; i < words_left_in_block; i++) + { + *dbuff_ptr++ = (float)source[short_ctr + 1] / this->max_value; + *dbuff_ptr++ = (float)source[short_ctr] / this->max_value; +#ifdef debug3 + if (i < 1) + { + + printf("1 I read %d, real %f, calc_int %f\n", + source[short_ctr], *(dbuff_ptr - 2), + (*(dbuff_ptr - 2) * this->max_value)); + printf("1 Q read %d, real %f, calc_int %f\n\n", + source[short_ctr + 1], *(dbuff_ptr - 1), + (*(dbuff_ptr - 1) * this->max_value)); + } +#endif + short_ctr += 2; + } + + buff_ptr = (char *)dbuff_ptr; + } + +#ifdef debug2 + int16_t *temp_ptr = (int16_t *)ringbuffer_ptr; + /* + for (int i=0; i < 10; i++) { + SoapySDR_logf(SOAPY_SDR_DEBUG, "A 0x%04X, %d ", *temp_ptr, + *temp_ptr); temp_ptr++; + } + printf("done \n"); + + */ + uint32_t words_left = words_left_in_block - 1; + + temp_ptr = (int16_t *)(ringbuffer_ptr + (bytes_left_in_block - 4)); + ptrdiff_t diff = (uint8_t *)temp_ptr - (uint8_t *)ringbuffer_ptr; + SoapySDR_logf(SOAPY_SDR_DEBUG, "tmp_ptr %p, diff %d", temp_ptr, diff); + + for (int i = 0; i < 10; i++) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "A %d %p, 0x%04X, %d ", words_left--, + temp_ptr, *temp_ptr, *temp_ptr); + temp_ptr--; + } + printf("done neg\n"); +#endif + + if (useShort == true) + { + buff_ptr = buff_ptr + bytes_left_in_block; + } + + numElemsLeft -= words_left_in_block; + + // move to the next buffer in the ring + rxReadIndex = (rxReadIndex + 1) % DEFAULT_NUM_BUFFERS; + p_rx_block_index = 0; + + if (numElemsLeft == 0) + { + break; + } + + // if no more data in ring buffer wait + while ((rxReadIndex == rxWriteIndex) && (waitTime > 0)) + { + usleep(DEFAULT_SLEEP_US); + waitTime -= DEFAULT_SLEEP_US; + } + if (waitTime <= 0) + { + SoapySDR_log(SOAPY_SDR_DEBUG, "readStream timed out"); + return SOAPY_SDR_TIMEOUT; + } + words_left_in_block = rx_payload_size_in_words; + ringbuffer_ptr = + (char *)p_rx_block[rxReadIndex]->data + p_rx_block_index; + } + + // we are here if we either finished the loop above or the amount left to + // copy is less than what we have in the block received. if we are here then + // there is at least one ring buffer available and we can send it without + // waiting + if (numElemsLeft < words_left_in_block && numElemsLeft != 0) + { +#ifdef debug + SoapySDR_logf(SOAPY_SDR_DEBUG, + "2 numElemsLeft %d, words_left_in_block %d", numElemsLeft, + words_left_in_block); + SoapySDR_logf(SOAPY_SDR_DEBUG, "rxReadIndex %d, rxWriteIndex %d", + rxReadIndex, rxWriteIndex); + + SoapySDR_logf(SOAPY_SDR_DEBUG, "buff_ptr %p, ringbuffer_ptr %p", + buff_ptr, ringbuffer_ptr); +#endif + + uint32_t numElemsLeft_in_bytes = numElemsLeft * 4; + + if (useShort == true) + { // CS16 + memcpy(buff_ptr, ringbuffer_ptr, numElemsLeft_in_bytes); + } + else + { // float + float * dbuff_ptr = (float *)buff_ptr; + int16_t *source = (int16_t *)ringbuffer_ptr; + + int short_ctr = 0; + for (uint32_t i = 0; i < numElemsLeft; i++) + { + *dbuff_ptr++ = (float)(source[short_ctr] / this->max_value); + *dbuff_ptr++ = (float)(source[short_ctr + 1] / this->max_value); +//#define debug3 +#ifdef debug3 + if (i < 2) + { + + printf("2 I read %d, real %f, dbuff_ptr %p\n", + source[short_ctr], *(dbuff_ptr - 2), + (dbuff_ptr - 2)); + printf("2 Q read %d, real %f, dbuff_ptr %p\n", + source[short_ctr + 1], *(dbuff_ptr - 1), + (dbuff_ptr - 1)); + } +#endif + + short_ctr += 2; + } + } + +#ifdef debug2 + int16_t *temp_ptr = (int16_t *)buff_ptr; + /* + for (int i=0; i < 10; i++) { + SoapySDR_logf(SOAPY_SDR_DEBUG, "B 0x%04X, ", *temp_ptr); + temp_ptr++; + } + printf("done \n"); + */ + uint32_t words_left = words_left_in_block - 1; + + temp_ptr = (int16_t *)(buff_ptr + numElemsLeft_in_bytes - 4); + for (int i = 0; i < 10; i++) + { + SoapySDR_logf(SOAPY_SDR_DEBUG, "A %d %p, 0x%04X, %d ", words_left--, + temp_ptr, *temp_ptr, *temp_ptr); + temp_ptr--; + } + printf("done neg\n"); +#endif + // keep this around for the next call + p_rx_block_index += numElemsLeft_in_bytes; + } + + // if we are here then we have put NumElems into the buffer + return numElems; } -int SoapySidekiq::writeStream(SoapySDR::Stream *stream, - const void *const *buffs, - const size_t numElems, - int &flags, - const long long timeNs, - const long timeoutUs) { +int SoapySidekiq::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; - } - - const uint32_t block_size_in_words = 1020; - - // tx data flow mode - if (skiq_write_tx_data_flow_mode(card, tx_hdl, skiq_tx_immediate_data_flow_mode) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_tx_data_flow_mode (card %d)", card); - } - // tx block size - if (skiq_write_tx_block_size(card, tx_hdl, block_size_in_words) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_tx_block_size (card %d)", card); - } - // transfer mode (sync for now) - if (skiq_write_tx_transfer_mode(card, tx_hdl, skiq_tx_transfer_mode_sync) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_tx_block_size (card %d)", card); - } - // tx attenuation - if (skiq_write_tx_attenuation(card, tx_hdl, 0) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_write_tx_attenuation (card %d)", card); - } - - size_t data_bytes = numElems * 2 * sizeof(int16_t); - uint32_t num_blocks = (data_bytes / (block_size_in_words * 2)); - if ((data_bytes % (block_size_in_words * 4)) == 0) { - num_blocks++; - } - - uint32_t i; - uint64_t timestamp = 0; - - for (i = 0; i < num_blocks; i++) { - skiq_tx_block_t *p_block = skiq_tx_block_allocate(block_size_in_words); - memcpy(p_block->data, buffs[0], block_size_in_words); - skiq_tx_set_block_timestamp(p_block, timestamp); - if (skiq_transmit(card, tx_hdl, p_block, NULL) != 0) { - SoapySDR_logf(SOAPY_SDR_ERROR, "Failure: skiq_transmit (card %d)", card); - } - timestamp += block_size_in_words; - } - - return numElems; + int status = 0; + uint32_t errors = 0; + + if (stream != TX_STREAM) + { + return SOAPY_SDR_NOT_SUPPORTED; + } + + // Pointer to the location in the input buffer to transmit from + char *inbuff_ptr = (char *)(buffs[0]); + +#ifdef debug2 + int16_t *tmp_ptr = (int16_t *)inbuff_ptr; + + uint32_t tmp_ctr = 0; + + for (int i = 0; i < 5; i++) + { + printf(" 0x%04X 0x%04X ", (uint16_t)tmp_ptr[tmp_ctr], + (uint16_t)tmp_ptr[tmp_ctr + 1]); + tmp_ctr += 2; + fflush(stdout); + } + printf("\n"); +#endif + + // Pointer to the location in the output buffer to copy to. + char *outbuff_ptr = + (char *)p_tx_block[currTXBuffIndex]->data + p_tx_block_index; + + // How many bytes are left in the block we are working on. + uint32_t block_bytes_left = + (DEFAULT_TX_BUFFER_LENGTH * 4) - p_tx_block_index; + + // total number of bytes that need to be transmitted in this call + uint32_t data_bytes = numElems * 4; + + /* loop until the number of elements to send is less than the size left in + * the block */ + while (data_bytes >= block_bytes_left) + { +#ifdef debug + SoapySDR_logf(SOAPY_SDR_DEBUG, + "1 numElems %u, data_bytes %d, block_bytes_left %d, " + "p_tx_block_index %u\n", + numElems, data_bytes, block_bytes_left, p_tx_block_index); + SoapySDR_logf(SOAPY_SDR_DEBUG, "inbuff_ptr %p, outbuff_ptr %p\n", + inbuff_ptr, outbuff_ptr); +#endif + + memcpy(outbuff_ptr, inbuff_ptr, block_bytes_left); + + status = skiq_transmit(card, tx_hdl, p_tx_block[currTXBuffIndex], NULL); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_transmit (card %d) status %d", card, + status); + } + + inbuff_ptr += block_bytes_left; + data_bytes -= block_bytes_left; + block_bytes_left = DEFAULT_TX_BUFFER_LENGTH * 4; + + p_tx_block_index = 0; + currTXBuffIndex = (currTXBuffIndex + 1) % DEFAULT_NUM_BUFFERS; + outbuff_ptr = (char *)p_tx_block[currTXBuffIndex]->data; + } + + // This means that the number of bytes left to send is smaller than what is + // left in the block So just copy the data into the block and be done. The + // rest will be sent on the next call + if (data_bytes < block_bytes_left && data_bytes != 0) + { +#ifdef debug + SoapySDR_logf( + SOAPY_SDR_DEBUG, + "2 data_bytes %d, block_bytes_left %d, p_tx_block_index %u\n", + data_bytes, block_bytes_left, p_tx_block_index); + SoapySDR_logf(SOAPY_SDR_DEBUG, "inbuff_ptr %p, outbuff_ptr %p\n", + inbuff_ptr, outbuff_ptr); +#endif + + memcpy(outbuff_ptr, inbuff_ptr, block_bytes_left); + p_tx_block_index += data_bytes; + } + + /* This call will return a cumulative number of underruns since start + * streaming */ + status = skiq_read_tx_num_underruns(card, tx_hdl, &errors); + if (status != 0) + { + SoapySDR_logf(SOAPY_SDR_ERROR, + "Failure: skiq_read_tx_num_underruns (card %d) status %d", + card, status); + } + + if (errors >= tx_underruns + 500) + { + printf("cumulative underruns %d\n", errors); + tx_underruns = errors; + } + + return numElems; } /******************************************************************* * Direct buffer access API ******************************************************************/ -size_t SoapySidekiq::getNumDirectAccessBuffers(SoapySDR::Stream *stream) { - std::lock_guard lock(_buf_mutex); - return _buffs.size(); +size_t SoapySidekiq::getNumDirectAccessBuffers(SoapySDR::Stream *stream) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getNumDirectAccessBuffers"); + return 0; } -int SoapySidekiq::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, const size_t handle, void **buffs) { - std::lock_guard lock(_buf_mutex); - buffs[0] = (void *) _buffs[handle].data(); - return 0; +int SoapySidekiq::getDirectAccessBufferAddrs(SoapySDR::Stream *stream, + const size_t handle, void **buffs) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "getDirectAccessBufferAddrs"); + return 0; } -int SoapySidekiq::acquireReadBuffer(SoapySDR::Stream *stream, - size_t &handle, - const void **buffs, - int &flags, - long long &timeNs, - const long timeoutUs) { - if (stream != RX_STREAM) { +int SoapySidekiq::acquireReadBuffer(SoapySDR::Stream *stream, size_t &handle, + const void **buffs, int &flags, + long long &timeNs, const long timeoutUs) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "acquireReadBuffer"); return SOAPY_SDR_NOT_SUPPORTED; - } - - std::unique_lock lock(_buf_mutex); - - // reset is issued by various settings - // overflow set in the rx thread - if (resetBuffer || _overflowEvent) { - SoapySDR_log(SOAPY_SDR_INFO, "Resetting all RX Buffers"); - // drain all buffers from the fifo - _buf_tail = 0; - _buf_head = 0; - _buf_count = 0; - for (auto &buff : _buffs) buff.clear(); - _overflowEvent = false; - if (resetBuffer) { - resetBuffer = false; - } else { - SoapySDR_log(SOAPY_SDR_SSI, "O"); - return SOAPY_SDR_OVERFLOW; - } - } - - // wait for a buffer to become available - if (_buf_count == 0) { - _buf_cond.wait_for(lock, std::chrono::microseconds(timeoutUs)); - if (_buf_count == 0) { - SoapySDR_logf(SOAPY_SDR_WARNING, "Read Timeout occured after %d ms", timeoutUs); - return SOAPY_SDR_TIMEOUT; - } - } - - // extract handle and buffer - handle = _buf_head; - buffs[0] = (void *) _buffs[handle].data(); - flags = 0; - - _buf_head = (_buf_head + 1) % numBuffers; - - // return number available - return static_cast(_buffs[handle].size() / (elementsPerSample * shortsPerWord)); } -void SoapySidekiq::releaseReadBuffer(SoapySDR::Stream *stream, const size_t handle) { - if (stream != RX_STREAM) { - throw std::runtime_error("Invalid stream"); - } - SoapySDR_logf(SOAPY_SDR_TRACE, "Release Read Buffer %d", handle); - std::lock_guard lock(_buf_mutex); - _buffs[handle].clear(); - _buf_count--; +void SoapySidekiq::releaseReadBuffer(SoapySDR::Stream *stream, + const size_t handle) +{ + if (stream != RX_STREAM) + { + throw std::runtime_error("Invalid stream"); + } + SoapySDR_logf(SOAPY_SDR_TRACE, "Release Read Buffer %d", handle); } -int SoapySidekiq::acquireWriteBuffer(SoapySDR::Stream *stream, - size_t &handle, - void **buffs, - const long timeoutUs) { - if (stream != TX_STREAM) { +int SoapySidekiq::acquireWriteBuffer(SoapySDR::Stream *stream, size_t &handle, + void **buffs, const long timeoutUs) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "acquireWriteBuffer"); return SOAPY_SDR_NOT_SUPPORTED; - } - - return -1; - } void SoapySidekiq::releaseWriteBuffer(SoapySDR::Stream *stream, - const size_t handle, - const size_t numElems, - int &flags, - const long long timeNs) { - if (stream != TX_STREAM) { - throw std::runtime_error("Invalid stream"); - } -} \ No newline at end of file + const size_t handle, + const size_t numElems, int &flags, + const long long timeNs) +{ + SoapySDR_logf(SOAPY_SDR_TRACE, "releaseWriteBuffer"); +} diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..0a2ec78 --- /dev/null +++ b/install.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +set -x +set -e + +sudo rm -rf Soapy* + +sudo apt update + +sudo apt-get install -y git + +sudo apt-get install -y cmake g++ libpython3-dev python3-numpy swig + +sudo apt install -y soapysdr-tools + +sudo apt-get install -y python3-dev python3-pip + +pip install numpy + +pip install pynmea + + +echo "cloning SoapySDR" +git clone https://github.com/pothosware/SoapySDR.git +cd SoapySDR +git checkout master +mkdir build +cd build +echo "Making SoapySDr" +cmake ../ +make +echo "Installing SoapySDr" +sudo make install + +cd ../../ + + + +echo "cloning SoapySidekiq" +git clone https://github.com/epiqsolutions/SoapySidekiq.git +cd SoapySidekiq +mkdir build +cd build +echo "Making SoapySidekiq" +cmake ../ +make +echo "Installing SoapySidekiq" +sudo make install + +sudo ldconfig + +cd ../ + +export LD_LIBRARY_PATH=/usr/lib/epiq:$LD_LIBRARY_PATH diff --git a/remake.sh b/remake.sh new file mode 100755 index 0000000..d0793fc --- /dev/null +++ b/remake.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +cd build +make clean +make +sudo make install diff --git a/tests/cf32_test b/tests/cf32_test new file mode 100755 index 0000000..502d571 --- /dev/null +++ b/tests/cf32_test @@ -0,0 +1,100 @@ +#!/bin/python3 + +import sys +import time +import numpy as np +from matplotlib import pyplot as plt +import SoapySDR +print(SoapySDR, __file__) +from SoapySDR import * + +np.set_printoptions(threshold=sys.maxsize) + +############################################################################################ +# Settings +############################################################################################ +# Data transfer settings +NUM_BUFF = 10 +rx_chan = 0 # RX1 = 0, RX2 = 1 +fs = 10e6 # Radio sample Rate +freq = 100e6 # LO tuning frequency in Hz +use_agc = False # Use or don't use the AGC +timeout_us = int(10e6) +rx_resolution = 12 +max_data = (1 << (rx_resolution -1))-1 +print(max_data) +SCALE = max_data + 1 + +############################################################################################ +# Receive Signal +############################################################################################ +sdr = SoapySDR.Device() +SoapySDR.setLogLevel(SOAPY_SDR_TRACE) + +sdr.writeSetting("counter", "true") +setting = sdr.readSetting("counter") +print("read counter", setting) + +sdr.setSampleRate(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setBandwidth(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +#sdr.setGainMode(SOAPY_SDR_RX, rx_chan, use_agc) # Set the gain mode +sdr.setFrequency(SOAPY_SDR_RX, rx_chan, freq) # Tune the LO + +# Create data buffer and start streaming samples to it +rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [rx_chan]) # Setup data stream + +# create a re-usable buffer for receiving samples +nb = sdr.getStreamMTU(rx_stream) +print("Stream MTU set to", nb) + +N = nb + +buff = np.empty([NUM_BUFF, N], dtype=np.csingle) +intbuff = np.empty((N*2), dtype=np.int16) +real = np.zeros(N, dtype=np.int16) +imag = np.zeros(N, dtype=np.int16) + +sdr.activateStream(rx_stream) # this turns the radio on + +for idx1 in range(NUM_BUFF): + print("read block", idx1) + + sr = sdr.readStream(rx_stream, [buff[idx1]], N) + + rc = sr.ret # number of samples read or the error code + assert rc == N, 'Error Reading Samples from Device (error code = %d)!' % rc + +sdr.deactivateStream(rx_stream) +sdr.closeStream(rx_stream) + +for i in range(NUM_BUFF): + print("analyze block", i) + + real = buff[i].real * SCALE + imag = buff[i].imag * SCALE + + intbuff[0::2] = real + intbuff[1::2] = imag + + expected = intbuff[0] + + #validate samples + for j in range(((2 * N) - 1)): + this_value = intbuff[j] + + if (this_value != expected): + print("bad value", j, "expected", expected, "value", this_value) + + #error print the buffer around the error + for k in range(-5, 5): + print((j + k), " ", intbuff[j+k]) + break + + expected = (this_value + 1) + if expected == (max_data + 1): + expected = -(max_data+1) + + if j >= ((2 * N - 1) - 1): + print(" success") + + diff --git a/tests/cs16_test b/tests/cs16_test new file mode 100755 index 0000000..e534c1d --- /dev/null +++ b/tests/cs16_test @@ -0,0 +1,95 @@ +#!/bin/python3 + +import sys +import time +import numpy as np +from matplotlib import pyplot as plt +import SoapySDR +print(SoapySDR, __file__) +from SoapySDR import * + +#np.set_printoptions(threshold=sys.maxsize) + +############################################################################################ +# Settings +############################################################################################ +# Data transfer settings +NUM_BUFF = 10 +rx_chan = 0 # RX1 = 0, RX2 = 1 +fs = 20e6 # Radio sample Rate +freq = 100e6 # LO tuning frequency in Hz +use_agc = True +timeout_us = int(10e6) +rx_resolution = 12 +max_data = (1 << (rx_resolution -1))-1 +print(max_data) + +############################################################################################ +# Receive Signal +############################################################################################ +sdr = SoapySDR.Device() +SoapySDR.setLogLevel(SOAPY_SDR_TRACE) + +setting = sdr.readSetting("iq_swap") +print("read iq_swap", setting) + +sdr.writeSetting("counter", "true") +setting = sdr.readSetting("counter") +print("read counter", setting) + +sdr.setSampleRate(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setBandwidth(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setGainMode(SOAPY_SDR_RX, rx_chan, use_agc) # Set the gain mode +sdr.setFrequency(SOAPY_SDR_RX, rx_chan, freq) # Tune the LO + +# Create data buffer and start streaming samples to it +rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [rx_chan]) # Setup data stream + +N = sdr.getStreamMTU(rx_stream) + +print("MTU", N) + + +rx_buff = np.empty([NUM_BUFF, (2 * N)], np.int16) # Create memory buffer for data stream + +sdr.activateStream(rx_stream) # this turns the radio on +for idx1 in range(NUM_BUFF): + print("read block", idx1) + + # Read the samples from the data buffer + sr = sdr.readStream(rx_stream, [rx_buff[idx1]], N, timeoutUs=timeout_us) + + rc = sr.ret # number of samples read or the error code + assert rc == N, 'Error Reading Samples from Device (error code = %d)!' % rc + +sdr.deactivateStream(rx_stream) + + +for i in range(NUM_BUFF): + print("analyze block", i) + print(rx_buff[i]) + expected = np.int16(rx_buff[i][0]) + #validate samples + for j in range(((2 * N) - 1)): + this_value = np.int16(rx_buff[i][j]) + + if (this_value != expected): + print(rx_buff[i]) + print("bad value", j, "expected", expected, "value", this_value) + + #error print the buffer around the error + for k in range(-5, 5): + print((j + k), " ", rx_buff[i][j+k]) + break + + expected = (this_value + 1) + if expected == (max_data + 1): + expected = -(max_data+1) + + if j >= ((2 * N - 1) - 1): + print(" success") + + +# Stop streaming +sdr.closeStream(rx_stream) + diff --git a/tests/peak_scan b/tests/peak_scan new file mode 100755 index 0000000..42009ed --- /dev/null +++ b/tests/peak_scan @@ -0,0 +1,278 @@ +#!/bin/python3 + +import sys +import csv +import time +import signal +import datetime +import argparse +import serial +import pynmea2 + +import numpy as np +import SoapySDR +from SoapySDR import * + +def read_gps(ser): + trying = True + fix = "no" + lat = "0"; + lat_dir = "" + lon = "0" + lon_dir = "" + timestamp = "" + + while(trying): + try: + line = ser.readline() + newline = str(line, 'utf-8', errors='ignore') + if "GGA" in newline: + data = pynmea2.parse(newline) + timestamp = data.timestamp + lat, lon, alt = data.latitude, data.longitude, data.altitude + lat_dir, lon_dir, num_sats = data.lat_dir, data.lon_dir, data.num_sats + gps_qual = data.gps_qual + if gps_qual != 0: + fix = "yes" + trying = False; + else: + fix = "no" + trying = False; + + except serial.SerialException as e: + trying = False; + + except serial.SerialTimeoutException as e: + trying = False; + + ret_lat = "{:4.6}".format(lat) + " " +lat_dir + ret_lon = "{:4.6}".format(lon) + " " + lon_dir + return fix, ret_lat, ret_lon, str(timestamp) + + +def handler(signum, frame): + global running, sdr, rx_stream + + print("...ending...") + running = False + + # Stop streaming + sdr.deactivateStream(rx_stream) + sdr.closeStream(rx_stream) + + exit(1) + +def compute_mag_spectrum(x, samp_rate_hz, fft_len): + # bin frequencies + t_samp = 1./samp_rate_hz + bin_freq_hz = np.fft.fftfreq(fft_len, t_samp) + + # get mag spectrum of windowed zero-padded input + window = np.hamming(len(x)) +# print("window", window.sum(), "fftlen", fft_len) + + x_zp = np.zeros((fft_len,), dtype=complex) + x_zp[:len(x)] = x * window + mag_spectrum = np.abs(np.fft.fft(x_zp, norm='ortho'))**2 + + # figure out scale factor (each factor is squared since applied to mag spectrum) + # so that 0 dBFS sinusoid will have mag spectrum peak at 0 dB + xlen_factor = len(x) + window_factor = 1./(window.sum()**2) + zp_factor = fft_len/len(x) + scale = xlen_factor * window_factor * zp_factor + + return (np.fft.fftshift(bin_freq_hz), np.fft.fftshift(scale*mag_spectrum)) + +def main(cardno, rx_chan, fs, bw, sfreq, stfreq, port, looptime): + global running, sdr, rx_stream + + # Figure out how many times to loop to get one sample across the frequency range + diff = stfreq - sfreq + + if diff < 0: + print("Error: start freq is bigger than stop freq") + exit(-1) + + # we loop using the sample rate, which must be bigger than the bandwidth. So there is + # overlap between each loop so we don't lose signals between loops. + if (diff < bw): + loops = 1 + else: + loops = int(diff / bw) + + print("Number of BW searches to scan the whole freq range: ", loops, "\n") + + ############################################################################################ + # Settings + ############################################################################################ + if (port != None): + print(port) + ser = serial.Serial(port, BAUD, timeout=2.0) + + running = True + + # Data transfer settings + N = 65536 # Number of complex samples per transfer + use_agc = False # Use or don't use the AGC + timeout_us = int(10e6) + rx_bits = 12 + + if (port == None): + header = "Local Date/Time, Peak Frequency, Peak Power (dB FS)\n" + else: + header = "Local Date/Time, Time(UTC), GPS Sat Lock, Latitude, Longitude, Peak Frequency, Peak Power (dB FS)\n" + + + signal.signal(signal.SIGINT, handler) + + args = dict(card = cardno) +# args = dict(serial = '8N55') + sdr = SoapySDR.Device(args) + + SoapySDR.setLogLevel(SOAPY_SDR_ERROR) + + # Create data buffer and start streaming samples to it + rx_buff = np.empty((loops,2 * N), np.int16) # Create memory buffer for data stream + peak_db = np.empty(loops) + peak_abs_freq_hz = np.empty(loops) + + f = open('output.csv', 'w', encoding='UTF8') + out = "Sample Rate, " + "{:10.0f}".format(fs) + "\n" + f.write(out) + out = "Bandwidth, "+ "{:10.0f}".format(bw) + "\n" + + f.write(out) + out = "Start Freq, " + "{:10.0f}".format(sfreq) + "\n" + f.write(out) + out = "Stop Freq, " + "{:10.0f}".format(stfreq) + "\n\n" + + f.write(out) + f.write(header) + + ############################################################################################ + # Receive Signal + ############################################################################################ + + sdr.setSampleRate(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate + sdr.setBandwidth(SOAPY_SDR_RX, rx_chan, bw) # Set sample rate + sdr.setGain(SOAPY_SDR_RX, rx_chan, 0) # Set the gain mode + + rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [rx_chan]) # Setup data stream + + while running: + max_index = 0 + max_power = -150 + + curFreq = sfreq + (bw/2) + + # Read the samples from the data buffer + for i in range(loops): + #print("reading stream", i, curFreq) + sdr.setFrequency(SOAPY_SDR_RX, rx_chan, curFreq ) # Tune the LO + + sdr.activateStream(rx_stream) # this turns the radio on + sr = sdr.readStream(rx_stream, [rx_buff[i]], N, timeoutUs=timeout_us) + sdr.deactivateStream(rx_stream) # this turns the radio off + + rc = sr.ret # number of samples read or the error code + assert rc == N, 'Error Reading Samples from Device (error code = %d)!' % rc + + # Convert interleaved shorts (received signal) to numpy.complex64 normalized between [-1, 1] + s0 = rx_buff[i].astype(np.int16) + x = s0[1::2] + 1j*s0[0::2] + + lo_freq = curFreq + samp_rate = fs + n_bits_iq = rx_bits + fft_len = N + + # read, scale, truncate IQ sequence + full_scale_mag = 2.**(n_bits_iq-1) + x /= full_scale_mag + x = x[:fft_len] + + # calc spectrum + bin_freq, bin_mag = compute_mag_spectrum(x, samp_rate, fft_len) + bin_mag_db = 10*np.log10(bin_mag) + + # find top peak and plot + idx = np.argmax(bin_mag_db) + peak_db[i] = bin_mag_db[idx] + peak_freq_hz = bin_freq[idx] + peak_abs_freq_hz[i] = lo_freq + peak_freq_hz + + if peak_db[i] > max_power: + max_index = i + max_power = peak_db[i] +# print(max_power, peak_abs_freq_hz[i]) + +# print(idx, "bin_freq", peak_freq_hz, "peak freq", peak_abs_freq_hz, "max_power", peak_db) + + curFreq += bw + + now = datetime.datetime.now().strftime("%d-%b-%Y (%H:%M:%S)") + print("local date/time", now) + + if (port != None): + fix, lat, lon, timestamp = read_gps(ser); + print("UTC Time", timestamp, "gps lock:", fix, "latitude:", lat, "longitude:", lon, ) + + print('Max Power (dBm): ', "{:.0f}".format(peak_db[max_index])) + print('at Freq (MHz):', "{:10.3f}".format(peak_abs_freq_hz[max_index] / 1e6)) + + + if (port == None): + row = now + ', ' + "{:10.3f}".format((peak_abs_freq_hz[max_index] / 1e6)) + ', ' + "{:.0f}".format(peak_db[max_index]) + ' \n' + else: + row = now + ', ' + timestamp + ", " + fix + ', ' + lat + ', ' + lon + ', ' + row = row + "{:10.3f}".format(peak_abs_freq_hz[max_index] / 1e6) + ', ' + "{:.0f}".format(peak_db[max_index]) + ' \n' + print("") + f.write(row) + time.sleep(looptime) + + +def parse_command_line_arguments(): + """ Create command line options """ + help_formatter = argparse.ArgumentDefaultsHelpFormatter + parser = argparse.ArgumentParser(description='scan for peaks, build a csv file ', + formatter_class=help_formatter) + parser.add_argument('-c', required=False, dest='card', + default='0', help=' Card') + parser.add_argument('-chan', type=int, required=False, dest='chan', + default=0, help=' Channel') + parser.add_argument('-s', type=float, required=False, dest='fs', + default=20e6, help='Sample Rate') + parser.add_argument('-bw', type=float, required=False, dest='bw', + default=18e6, help='Bandwidth') + parser.add_argument('-sf', type=float, required=False, dest='sfreq', + default=1000e6, help='Starting Frequency') + parser.add_argument('-stf', type=float, required=False, dest='stfreq', + default=2000e6, help='Stopping Frequency') + parser.add_argument('-p', required=False, dest='port', + default=None, help='GPS Port') + parser.add_argument('-t', type=int, required=False, dest='looptime', + default=3, help='Time between tests') + return parser.parse_args(sys.argv[1:]) + + +if __name__ == '__main__': + + PORT = "/dev/ttySKIQ_UART1" + BAUD = 9600 + + pars = parse_command_line_arguments() + + if (pars.fs <= pars.bw): + print("Warning: Bandwidth must be smaller than the sample rate, Setting bandwidth to 80% of sample rate.") + + pars.bw = 0.8 * pars.fs + + print("card (-c)\t\t:", pars.card, "\t\t\tchannel (-chan)\t\t:", pars.chan) + print("sample rate (-s)\t:", pars.fs/1000000, "M","\t\tbandwidth (-bw)\t\t:", pars.bw/1000000, "M") + print("start freq (-sf)\t:", pars.sfreq/1000000, "M\t\tstop freq (-stf)\t:", pars.stfreq/1000000, "M") + print("UART port (-p)\t\t:", pars.port, "\ttime between tests (-t)\t:", pars.looptime, "sec\n") + + main(pars.card, pars.chan, pars.fs, pars.bw, pars.sfreq, pars.stfreq, pars.port, pars.looptime) + + f.close() diff --git a/tests/peak_scan_z3u_bin b/tests/peak_scan_z3u_bin new file mode 100755 index 0000000..43f28af Binary files /dev/null and b/tests/peak_scan_z3u_bin differ diff --git a/tests/rcv b/tests/rcv new file mode 100755 index 0000000..154ee2a --- /dev/null +++ b/tests/rcv @@ -0,0 +1,101 @@ +#!/bin/python3 + +import sys +import numpy as np +from matplotlib import pyplot as plt +import SoapySDR +print(SoapySDR, __file__) +from SoapySDR import * + +np.set_printoptions(threshold=sys.maxsize) + +############################################################################################ +# Settings +############################################################################################ +# Data transfer settings +rx_chan = 0 # RX1 = 0, RX2 = 1 +N = 32768 # Number of complex samples per transfer +N = 2048 # Number of complex samples per transfer +fs = 20e6 # Radio sample Rate +freq = 1000e6 # LO tuning frequency in Hz +use_agc = False # Use or don't use the AGC +timeout_us = int(10e6) +rx_bits = 12 + +############################################################################################ +# Receive Signal +############################################################################################ + +#if no args, then the code will find the first device available +SoapySDR.setLogLevel(SOAPY_SDR_TRACE) + +#if you want to find a card by card number then +args = dict(card = '1') +sdr = SoapySDR.Device(args) + +#if you want to find a card by serial number then +""" +args = dict(serial = '8N55') +sdr = SoapySDR.Device(args) +""" + + +#sdr = SoapySDR.Device() + +sdr.setSampleRate(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setBandwidth(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setGain(SOAPY_SDR_RX, rx_chan, 0) # Set the gain mode +sdr.setFrequency(SOAPY_SDR_RX, rx_chan, freq) # Tune the LO + +# Create data buffer and start streaming samples to it +rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [rx_chan]) # Setup data stream +#rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CF32, [rx_chan]) # Setup data stream +sdr.activateStream(rx_stream) # this turns the radio on + +rx_buff = np.empty(2 * N, np.int16) # Create memory buffer for data stream +#rx_buff = np.empty(2 * N, float) # Create memory buffer for data stream +# Read the samples from the data buffer +sr = sdr.readStream(rx_stream, [rx_buff], N, timeoutUs=timeout_us) +rc = sr.ret # number of samples read or the error code +assert rc == N, 'Error Reading Samples from Device (error code = %d)!' % rc + +# Stop streaming +sdr.deactivateStream(rx_stream) +sdr.closeStream(rx_stream) + +############################################################################################ +# Plot Signal +############################################################################################ +# Convert interleaved shorts (received signal) to numpy.complex64 normalized between [-1, 1] +#s0 = rx_buff.astype(float) / np.power(2.0, rx_bits-1) +s0 = rx_buff.astype(np.int16) / np.power(2.0, rx_bits-1) +s = (s0[1::2] + 1j*s0[::2]) + + +# Take the fourier transform of the signal and perform FFT Shift +S = np.fft.fftshift(np.fft.fft(s, N) / N) + +p = 20*np.log10((np.abs(S))) +f_mhz = (freq + (np.arange(0, fs, fs/N) - (fs/2) + (fs/N))) / 1e6 + +#print(np.c_[f_mhz, p]) +print(p.max()) + +# Time Domain Plot +plt.figure(num=1, figsize=(10, 5), dpi=150) +plt.subplot(211) +t_us = np.arange(N) / fs / 1e-6 +plt.plot(t_us, s.real, 'k', label='I') +plt.plot(t_us, s.imag, 'r', label='Q') +plt.xlim(t_us[0], t_us[-1]) +plt.xlabel('Time (us)') +plt.ylabel('Normalized Amplitude') + +# Frequency Domain Plot +plt.subplot(212) +plt.plot(f_mhz, p) +plt.xlim(f_mhz[0], f_mhz[-1]) +plt.ylim(-200, 0) +plt.xlabel('Frequency (MHz)') +plt.ylabel('Amplitude (dBFS)') +plt.show() diff --git a/tests/soapy_log_handle.py b/tests/soapy_log_handle.py new file mode 100644 index 0000000..7ece0b7 --- /dev/null +++ b/tests/soapy_log_handle.py @@ -0,0 +1,41 @@ +"""Example Python basing logging for SoapySDR. + +This permits exceptions to be thrown if the log message is severe enough. +""" +from __future__ import print_function + +import sys +import SoapySDR +from SoapySDR import * #SOAPY_SDR_ constants + +class SoapyException(Exception): + """SoapySDR has logged an error message (or worse).""" + +def set_python_log_handler(exception_level=None): + """Replace the default SoapySDR log handler with a Python one. + + The python handler sends the log text to stderr. + + If the log_level is at exception_level or worse then a SoapyException + is thrown. + """ + + log_level_text = { + SOAPY_SDR_FATAL: "FATAL", + SOAPY_SDR_CRITICAL: "CRITICAL", + SOAPY_SDR_ERROR: "ERROR", + SOAPY_SDR_WARNING: "WARNING", + SOAPY_SDR_NOTICE: "NOTICE", + SOAPY_SDR_INFO: "INFO", + SOAPY_SDR_DEBUG: "DEBUG", + SOAPY_SDR_TRACE: "TRACE", + SOAPY_SDR_SSI: "SSI"} + + def log_handler(log_level, message): + level_text = log_level_text[log_level] + log_text = "[{}] {}".format(level_text, message) + print(log_text, file=sys.stderr) + if exception_level is not None and log_level <= exception_level: + raise SoapyException(log_text) + + SoapySDR.registerLogHandler(log_handler) diff --git a/tests/test_api b/tests/test_api new file mode 100755 index 0000000..397fea6 --- /dev/null +++ b/tests/test_api @@ -0,0 +1,154 @@ +#!/bin/python3 + +import sys +import time +import numpy as np +from matplotlib import pyplot as plt +import SoapySDR +print(SoapySDR, __file__) +from SoapySDR import * + +""" +############################################################################################ +# Settings +############################################################################################ +# Data transfer settings +NUM_BUFF = 10 +rx_chan = 0 # RX1 = 0, RX2 = 1 +fs = 20e6 # Radio sample Rate +freq = 100e6 # LO tuning frequency in Hz +use_agc = True +timeout_us = int(10e6) +rx_resolution = 12 +max_data = (1 << (rx_resolution -1))-1 +print(max_data) +""" + +SoapySDR.setLogLevel(SOAPY_SDR_TRACE) +MAX_DEVICES = 10 +# find all the devices in the system +print("\t *****finding all devices******") +devices = 0 +devInfo = [dict() for x in range(MAX_DEVICES)] +for dev in SoapySDR.Device.enumerate(""): + devInfo[devices] = dev + devices += 1 + +print("found", devices, "devices") + +# loop through all the devices using card number and get info +print("\n\n\t *****get hardware info by card ******") +for idx in range(devices): + card = dict(card = str(idx)) + sdr = SoapySDR.Device(card) + args = sdr.getHardwareInfo() + print("***** hardwareInfo *****\n", args) + del sdr + + +# loop through all the devices using serial number and get info +print("\n\n\t *****get info by serial ******") +for idx in range(devices): + serial = dict(serial = (devInfo[idx]["serial"])) + sdr = SoapySDR.Device(serial) + rxchans = sdr.getNumChannels(SOAPY_SDR_RX) + txchans = sdr.getNumChannels(SOAPY_SDR_TX) + + + print("\nSerial Number:", serial) + print("hardwareInfo: ", args) + print("getNumChannels RX:", rxchans) + print("getNumChannels TX:", txchans) + print("\n") + del sdr + + + + + + + +""" +for i in range() +#if you want to find a card by card number then +""" +""" +args = dict(card = '1') +sdr = SoapySDR.Device(args) +""" + +#if you want to find a card by serial number then +""" +args = dict(serial = '8N55') +sdr = SoapySDR.Device(args) +""" +""" +#if you don't care +sdr = SoapySDR.Device() + + +sdr = SoapySDR.Device() + +setting = sdr.readSetting("iq_swap") +print("read iq_swap", setting) + +sdr.writeSetting("counter", "true") +setting = sdr.readSetting("counter") +print("read counter", setting) + +sdr.setSampleRate(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setBandwidth(SOAPY_SDR_RX, rx_chan, fs) # Set sample rate +sdr.setGainMode(SOAPY_SDR_RX, rx_chan, use_agc) # Set the gain mode +sdr.setFrequency(SOAPY_SDR_RX, rx_chan, freq) # Tune the LO + +# Create data buffer and start streaming samples to it +rx_stream = sdr.setupStream(SOAPY_SDR_RX, SOAPY_SDR_CS16, [rx_chan]) # Setup data stream + +N = sdr.getStreamMTU(rx_stream) + +print("MTU", N) + + +rx_buff = np.empty([NUM_BUFF, (2 * N)], np.int16) # Create memory buffer for data stream + +sdr.activateStream(rx_stream) # this turns the radio on +for idx1 in range(NUM_BUFF): + print("read block", idx1) + + # Read the samples from the data buffer + sr = sdr.readStream(rx_stream, [rx_buff[idx1]], N, timeoutUs=timeout_us) + + rc = sr.ret # number of samples read or the error code + assert rc == N, 'Error Reading Samples from Device (error code = %d)!' % rc + +sdr.deactivateStream(rx_stream) + + +for i in range(NUM_BUFF): + print("analyze block", i) + print(rx_buff[i]) + expected = np.int16(rx_buff[i][0]) + #validate samples + for j in range(((2 * N) - 1)): + this_value = np.int16(rx_buff[i][j]) + + if (this_value != expected): + print(rx_buff[i]) + print("bad value", j, "expected", expected, "value", this_value) + + #error print the buffer around the error + for k in range(-5, 5): + print((j + k), " ", rx_buff[i][j+k]) + break + + expected = (this_value + 1) + if expected == (max_data + 1): + expected = -(max_data+1) + + if j >= ((2 * N - 1) - 1): + print(" success") + +# Stop streaming +sdr.closeStream(rx_stream) +""" + diff --git a/tests/txtone b/tests/txtone new file mode 100755 index 0000000..0167310 --- /dev/null +++ b/tests/txtone @@ -0,0 +1,113 @@ +#!/usr/bin/env python3 + +""" +Transmits a tone out of the AIR-T. The script will create a tone segment that +is infinity repeatable without a phase discontinuity and with 8 samples per +period. The TX LO of the AIR-T is set such that the baseband frequency of the +generated tone plus the LO frequency will transmit at the desired RF. +""" +import sys +import time +import numpy as np +import argparse +import SoapySDR +from SoapySDR import * + +#N = 8188 +N = 8192 +#N = (8188 * 4) + 32 + + +def make_tone(n, fcen, fs, phi=0.285): + """ + Generates tone signal window with a frequency that is an integer + multiple of the sample rate so it can be repeated without a phase + discontinuity. + """ + period = fs / fcen + #assert n % period == 0, 'Total samples not integer number of periods' + a = 2**12 + # Make Complex Valued Tone Signal + wt = np.array(2 * np.pi * fcen * np.arange(n) / fs) + sig_cplx = np.exp(1j * (wt + phi)) + # Convert to interleaved int16 values + sig_int16 = np.empty(2 * n, dtype=np.int16) + sig_int16[0::2] = a * sig_cplx.real + sig_int16[1::2] = a * sig_cplx.imag + return sig_int16 + + +def transmit_tone(freq=1e7, chan=0, fs=31.25, attenuation=20, buff_len=16384): + """ Transmit a tone out of the Sidekiq """ + + sdr = SoapySDR.Device() + SoapySDR.setLogLevel(SOAPY_SDR_TRACE) +# SoapySDR.setLogLevel(SOAPY_SDR_ERROR) + + tx_stream = sdr.setupStream(SOAPY_SDR_TX, SOAPY_SDR_CS16, [chan]) + sdr.activateStream(tx_stream) # this turns the radio on + ##buff_len=(sdr.getStreamMTU(tx_stream)*4) + + buff_len = N + print("buff_len", buff_len) + + # Generate tone buffer that can be repeated without phase discontinuity + bb_freq = fs / 4 # baseband frequency of tone + tx_buff = make_tone(buff_len, bb_freq, fs) + lo_freq = freq # Calc LO freq to put tone at tone_rf + print("lo_freq", lo_freq, "tone_freq ", bb_freq, "buff_len", buff_len, "fs", fs, ) + + # Setup Radio + sdr.setSampleRate(SOAPY_SDR_TX, chan, fs) # Set sample rate + sdr.setBandwidth(SOAPY_SDR_TX, chan, fs) + sdr.setFrequency(SOAPY_SDR_TX, chan, lo_freq) # Tune the LO + sdr.setGain(SOAPY_SDR_TX, chan, attenuation) + + time_last_print = time.time() + total_samps = 0 + + ctr = 0 + # Transmit + print('Now Transmitting a tone at ', (fs/4000000), "MHz above LO frequency") + while True: + try: + rc = sdr.writeStream(tx_stream, [tx_buff], buff_len) + if rc.ret != buff_len: + print('TX Error {}: {}'.format(rc.ret, errToStr(rc.ret))) + except KeyboardInterrupt: + break + total_samps += buff_len + if time.time() > time_last_print + 5.0: + rate = total_samps / (time.time() - time_last_print) / 1e6 + print("measured sample rate: %f Msps" % rate) + total_samps = 0 + time_last_print = time.time() + + + + print("Stop streaming") + sdr.deactivateStream(tx_stream) + sdr.closeStream(tx_stream) + + +def parse_command_line_arguments(): + """ Create command line options for transmit function """ + help_formatter = argparse.ArgumentDefaultsHelpFormatter + parser = argparse.ArgumentParser(description='Transmit a tone on the AIR-T', + formatter_class=help_formatter) + parser.add_argument('-f', type=float, required=False, dest='freq', + default=1000e6, help='Lo Frequency') + parser.add_argument('-c', type=int, required=False, dest='chan', + default=0, help='TX Channel Number [0 or 1]') + parser.add_argument('-s', type=float, required=False, dest='fs', + default=40e6, help='TX Sample Rate') + parser.add_argument('-a', type=float, required=False, dest='attenuation', + default=0, help='TX attenuation') + parser.add_argument('-n', type=int, required=False, dest='buff_len', + default=16384, help='TX Buffer Size') + return parser.parse_args(sys.argv[1:]) + + +if __name__ == '__main__': + pars = parse_command_line_arguments() + transmit_tone(pars.freq, pars.chan, pars.fs, pars.attenuation, pars.buff_len)