diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..176a458f9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto diff --git a/cmake/Modules/FindLibIIO.cmake b/cmake/Modules/FindLibIIO.cmake new file mode 100644 index 000000000..358236e32 --- /dev/null +++ b/cmake/Modules/FindLibIIO.cmake @@ -0,0 +1,28 @@ +if(NOT LIBIIO_FOUND) + + pkg_check_modules (LIBIIO_PKG libiio) + find_path(LIBIIO_INCLUDE_DIR NAMES iio.h + PATHS + ${LIBIIO_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBIIO_LIBRARIES NAMES iio + PATHS + ${LIBIIO_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + + if(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES) + set(LIBIIO_FOUND TRUE CACHE INTERNAL "libiio found") + message(STATUS "Found libiio: ${LIBIIO_INCLUDE_DIR}, ${LIBIIO_LIBRARIES}") + else(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES) + set(LIBIIO_FOUND FALSE CACHE INTERNAL "libiio found") + message(STATUS "libiio not found.") + endif(LIBIIO_INCLUDE_DIR AND LIBIIO_LIBRARIES) + + mark_as_advanced(LIBIIO_INCLUDE_DIR LIBIIO_LIBRARIES) + +endif(NOT LIBIIO_FOUND) diff --git a/devices/CMakeLists.txt b/devices/CMakeLists.txt index c87807943..ebd573256 100644 --- a/devices/CMakeLists.txt +++ b/devices/CMakeLists.txt @@ -6,6 +6,7 @@ if (BUILD_DEBIAN) add_subdirectory(bladerf) add_subdirectory(hackrf) add_subdirectory(limesdr) + add_subdirectory(plutosdr) else(BUILD_DEBIAN) find_package(LibBLADERF) if(LIBUSB_FOUND AND LIBBLADERF_FOUND) @@ -22,4 +23,9 @@ else(BUILD_DEBIAN) add_subdirectory(limesdr) endif(LIBUSB_FOUND AND LIMESUITE_FOUND) + find_package(LibIIO) + if(LIBUSB_FOUND AND LIBIIO_FOUND) + add_subdirectory(plutosdr) + endif(LIBUSB_FOUND AND LIBIIO_FOUND) + endif (BUILD_DEBIAN) diff --git a/devices/plutosdr/CMakeLists.txt b/devices/plutosdr/CMakeLists.txt new file mode 100644 index 000000000..6ad4a038c --- /dev/null +++ b/devices/plutosdr/CMakeLists.txt @@ -0,0 +1,48 @@ +project(plutosdrdevice) + +set (CMAKE_CXX_STANDARD 11) + +set(plutosdrdevice_SOURCES + deviceplutosdrbox.cpp + deviceplutosdrscan.cpp +) + +set(plutosdrdevice_HEADERS + deviceplutsdrobox.h + deviceplutosdrscan.h +) + +if (BUILD_DEBIAN) +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${LIBIIOSRC} +) +else (BUILD_DEBIAN) +include_directories( + . + ${CMAKE_CURRENT_BINARY_DIR} + ${LIBIIO_INCLUDE_DIR} +) +endif (BUILD_DEBIAN) + +#add_definitions(${QT_DEFINITIONS}) +#add_definitions(-DQT_SHARED) + +add_library(plutosdrdevice SHARED + ${plutosdrdevice_SOURCES} +) + +if (BUILD_DEBIAN) +target_link_libraries(plutosdrdevice + libiio + sdrbase +) +else (BUILD_DEBIAN) +target_link_libraries(plutosdrdevice + ${LIBIIO_LIBRARIES} + sdrbase +) +endif (BUILD_DEBIAN) + +install(TARGETS plutosdrdevice DESTINATION lib) diff --git a/devices/plutosdr/deviceplutosdrbox.cpp b/devices/plutosdr/deviceplutosdrbox.cpp new file mode 100644 index 000000000..c5c1d294d --- /dev/null +++ b/devices/plutosdr/deviceplutosdrbox.cpp @@ -0,0 +1,280 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "deviceplutosdrbox.h" + +DevicePlutoSDRBox::DevicePlutoSDRBox(const std::string& uri) : + m_chnRx0(0), + m_chnTx0(0), + m_rxBuf(0), + m_txBuf(0) +{ + m_ctx = iio_create_context_from_uri(uri.c_str()); + m_devPhy = iio_context_find_device(m_ctx, "ad9361-phy"); + m_devRx = iio_context_find_device(m_ctx, "cf-ad9361-lpc"); + m_devTx = iio_context_find_device(m_ctx, "cf-ad9361-dds-core-lpc"); + m_valid = m_ctx && m_devPhy && m_devRx && m_devTx; +} + +DevicePlutoSDRBox::~DevicePlutoSDRBox() +{ + deleteRxBuffer(); + deleteTxBuffer(); + closeRx(); + closeTx(); + if (m_ctx) { iio_context_destroy(m_ctx); } +} + +void DevicePlutoSDRBox::set_params(DeviceType devType, + const std::vector& params) +{ + iio_device *dev; + + switch (devType) + { + case DEVICE_PHY: + dev = m_devPhy; + break; + case DEVICE_RX: + dev = m_devRx; + break; + case DEVICE_TX: + dev = m_devTx; + break; + default: + dev = m_devPhy; + break; + } + + for (std::vector::const_iterator it = params.begin(); it != params.end(); ++it) + { + struct iio_channel *chn = 0; + const char *attr = 0; + std::size_t pos; + int ret; + + pos = it->find('='); + + if (pos == std::string::npos) + { + std::cerr << "PlutoSDRDevice::set_params: Misformed line: " << *it << std::endl; + continue; + } + + std::string key = it->substr(0, pos); + std::string val = it->substr(pos + 1, std::string::npos); + + ret = iio_device_identify_filename(dev, key.c_str(), &chn, &attr); + + if (ret) + { + std::cerr << "PlutoSDRDevice::set_params: Parameter not recognized: " << key << std::endl; + continue; + } + + if (chn) { + ret = iio_channel_attr_write(chn, attr, val.c_str()); + } else if (iio_device_find_attr(dev, attr)) { + ret = iio_device_attr_write(dev, attr, val.c_str()); + } else { + ret = iio_device_debug_attr_write(dev, attr, val.c_str()); + } + + if (ret < 0) + { + std::cerr << "PlutoSDRDevice::set_params: Unable to write attribute " << key << ": " << ret << std::endl; + } + } +} + +bool DevicePlutoSDRBox::openRx() +{ + if (!m_chnRx0) { + m_chnRx0 = iio_device_find_channel(m_devRx, "voltage0", false); + } + + if (m_chnRx0) { + iio_channel_enable(m_chnRx0); + return true; + } else { + std::cerr << "PlutoSDRDevice::openRx: failed" << std::endl; + return false; + } +} + +bool DevicePlutoSDRBox::openTx() +{ + if (!m_chnTx0) { + m_chnTx0 = iio_device_find_channel(m_devTx, "voltage0", true); + } + + if (m_chnTx0) { + iio_channel_enable(m_chnTx0); + return true; + } else { + std::cerr << "PlutoSDRDevice::openTx: failed" << std::endl; + return false; + } +} + +void DevicePlutoSDRBox::closeRx() +{ + if (m_chnRx0) { iio_channel_disable(m_chnRx0); } +} + +void DevicePlutoSDRBox::closeTx() +{ + if (m_chnTx0) { iio_channel_disable(m_chnTx0); } +} + +struct iio_buffer *DevicePlutoSDRBox::createRxBuffer(unsigned int size, bool cyclic) +{ + if (m_devRx) { + m_rxBuf = iio_device_create_buffer(m_devRx, size, cyclic ? '\1' : '\0'); + } else { + m_rxBuf = 0; + } + + return m_rxBuf; +} + +struct iio_buffer *DevicePlutoSDRBox::createTxBuffer(unsigned int size, bool cyclic) +{ + if (m_devTx) { + m_txBuf = iio_device_create_buffer(m_devTx, size, cyclic ? '\1' : '\0'); + } else { + m_txBuf = 0; + } + + return m_txBuf; +} + +void DevicePlutoSDRBox::deleteRxBuffer() +{ + if (m_rxBuf) { + iio_buffer_destroy(m_rxBuf); + m_rxBuf = 0; + } +} + +void DevicePlutoSDRBox::deleteTxBuffer() +{ + if (m_txBuf) { + iio_buffer_destroy(m_txBuf); + m_txBuf = 0; + } +} + +ssize_t DevicePlutoSDRBox::getRxSampleSize() +{ + if (m_devRx) { + return iio_device_get_sample_size(m_devRx); + } else { + return 0; + } +} + +ssize_t DevicePlutoSDRBox::getTxSampleSize() +{ + if (m_devTx) { + return iio_device_get_sample_size(m_devTx); + } else { + return 0; + } +} + +ssize_t DevicePlutoSDRBox::rxBufferRefill() +{ + if (m_rxBuf) { + return iio_buffer_refill(m_rxBuf); + } else { + return 0; + } +} + +ssize_t DevicePlutoSDRBox::txBufferPush() +{ + if (m_txBuf) { + return iio_buffer_push(m_txBuf); + } else { + return 0; + } +} + +std::ptrdiff_t DevicePlutoSDRBox::rxBufferStep() +{ + if (m_rxBuf) { + return iio_buffer_step(m_rxBuf); + } else { + return 0; + } +} + +char* DevicePlutoSDRBox::rxBufferEnd() +{ + if (m_rxBuf) { + return (char *) iio_buffer_end(m_rxBuf); + } else { + return 0; + } +} + +char* DevicePlutoSDRBox::rxBufferFirst() +{ + if (m_rxBuf) { + return (char *) iio_buffer_first(m_rxBuf, m_chnRx0); + } else { + return 0; + } +} + +std::ptrdiff_t DevicePlutoSDRBox::txBufferStep() +{ + if (m_txBuf) { + return iio_buffer_step(m_txBuf); + } else { + return 0; + } +} + +char* DevicePlutoSDRBox::txBufferEnd() +{ + if (m_txBuf) { + return (char *) iio_buffer_end(m_txBuf); + } else { + return 0; + } +} + +char* DevicePlutoSDRBox::txBufferFirst() +{ + if (m_txBuf) { + return (char *) iio_buffer_first(m_txBuf, m_chnTx0); + } else { + return 0; + } +} + + + + + diff --git a/devices/plutosdr/deviceplutosdrbox.h b/devices/plutosdr/deviceplutosdrbox.h new file mode 100644 index 000000000..71c12ce3b --- /dev/null +++ b/devices/plutosdr/deviceplutosdrbox.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_ +#define DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_ + +#include "deviceplutosdrscan.h" + +class DevicePlutoSDRBox +{ +public: + typedef enum + { + DEVICE_PHY, + DEVICE_RX, + DEVICE_TX + } DeviceType; + + struct Sample { + int16_t i; + int16_t q; + }; + + DevicePlutoSDRBox(const std::string& uri); + ~DevicePlutoSDRBox(); + bool isValid() const { return m_valid; } + + void set_params(DeviceType devType, const std::vector ¶ms); + bool openRx(); + bool openTx(); + void closeRx(); + void closeTx(); + struct iio_buffer *createRxBuffer(unsigned int size, bool cyclic); + struct iio_buffer *createTxBuffer(unsigned int size, bool cyclic); + void deleteRxBuffer(); + void deleteTxBuffer(); + ssize_t getRxSampleSize(); + ssize_t getTxSampleSize(); + struct iio_channel *getRxChannel0() { return m_chnRx0; } + struct iio_channel *getTxChannel0() { return m_chnTx0; } + ssize_t rxBufferRefill(); + ssize_t txBufferPush(); + std::ptrdiff_t rxBufferStep(); + char* rxBufferEnd(); + char* rxBufferFirst(); + std::ptrdiff_t txBufferStep(); + char* txBufferEnd(); + char* txBufferFirst(); + +private: + struct iio_context *m_ctx; + struct iio_device *m_devPhy; + struct iio_device *m_devRx; + struct iio_device *m_devTx; + struct iio_channel *m_chnRx0; + struct iio_channel *m_chnTx0; + struct iio_buffer *m_rxBuf; + struct iio_buffer *m_txBuf; + bool m_valid; +}; + +#endif /* DEVICES_PLUTOSDR_DEVICEPLUTOSDRBOX_H_ */ diff --git a/devices/plutosdr/deviceplutosdrscan.cpp b/devices/plutosdr/deviceplutosdrscan.cpp new file mode 100644 index 000000000..e14d8e8e7 --- /dev/null +++ b/devices/plutosdr/deviceplutosdrscan.cpp @@ -0,0 +1,115 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +#include "deviceplutosdrscan.h" + +void DevicePlutoSDRScan::scan() +{ + int i, num_contexts; + struct iio_scan_context *scan_ctx; + struct iio_context_info **info; + + scan_ctx = iio_create_scan_context(0, 0); + + if (!scan_ctx) + { + std::cerr << "PlutoSDRScan::scan: could not create scan context" << std::endl; + return; + } + + num_contexts = iio_scan_context_get_info_list(scan_ctx, &info); + + if (num_contexts < 0) + { + std::cerr << "PlutoSDRScan::scan: could not get contexts" << std::endl; + return; + } + + for (i = 0; i < num_contexts; i++) + { + const char *description = iio_context_info_get_description(info[i]); + const char *uri = iio_context_info_get_uri(info[i]); + printf("PlutoSDRScan::scan: %d: %s [%s]\n", i, description, uri); + char *pch = strstr(const_cast(description), "PlutoSDR"); + + if (pch) + { + m_scans.push_back({std::string(description), std::string("TBD"), std::string(uri)}); + m_urilMap[m_scans.back().m_uri] = &m_scans.back(); + + std::regex desc_regex(".*serial=(.+)"); + std::smatch desc_match; + std::regex_search(m_scans.back().m_name, desc_match, desc_regex); + + if (desc_match.size() == 2) + { + m_scans.back().m_serial = desc_match[1]; + m_serialMap[m_scans.back().m_serial] = &m_scans.back(); + } + } + } + + iio_context_info_list_free(info); + iio_scan_context_destroy(scan_ctx); +} + +const std::string* DevicePlutoSDRScan::getURIAt(unsigned int index) const +{ + if (index < m_scans.size()) { + return &(m_scans[index].m_uri); + } else { + return 0; + } +} + +const std::string* DevicePlutoSDRScan::getSerialAt(unsigned int index) const +{ + if (index < m_scans.size()) { + return &(m_scans[index].m_serial); + } else { + return 0; + } +} + +const std::string* DevicePlutoSDRScan::getURIFromSerial( + const std::string& serial) const +{ + std::map::const_iterator it = m_serialMap.find(serial); + if (it == m_serialMap.end()) { + return 0; + } else { + return &((it->second)->m_uri); + } +} + +void DevicePlutoSDRScan::getSerials(std::vector& serials) const +{ + std::vector::const_iterator it = m_scans.begin(); + serials.clear(); + + for (; it != m_scans.end(); ++it) { + serials.push_back(it->m_serial); + } +} + + + diff --git a/devices/plutosdr/deviceplutosdrscan.h b/devices/plutosdr/deviceplutosdrscan.h new file mode 100644 index 000000000..20384abc9 --- /dev/null +++ b/devices/plutosdr/deviceplutosdrscan.h @@ -0,0 +1,45 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2017 Edouard Griffiths, F4EXB // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_ +#define DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_ + +class DevicePlutoSDRScan +{ +public: + struct DeviceScan + { + std::string m_name; + std::string m_serial; + std::string m_uri; + }; + + void scan(); + int getNbDevices() const { return m_scans.size(); } + const std::string* getURIAt(unsigned int index) const; + const std::string* getSerialAt(unsigned int index) const ; + const std::string* getURIFromSerial(const std::string& serial) const; + void getSerials(std::vector& serials) const; + +private: + std::vector m_scans; + std::map m_serialMap; + std::map m_urilMap; +}; + + + +#endif /* DEVICES_PLUTOSDR_DEVICEPLUTOSDRSCAN_H_ */