diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dcd4d375..669c2ae43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,7 @@ option(ENABLE_ARRAY "Enable the use of CTTC's antenna array front-end as signal option(ENABLE_GN3S "Enable the use of the GN3S dongle as signal source (experimental)" OFF) option(ENABLE_PLUTOSDR "Enable the use of ADALM-PLUTO Evaluation Boards (Analog Devices Inc.), requires gr-iio" OFF) option(ENABLE_FMCOMMS2 "Enable the use of FMCOMMS4-EBZ + ZedBoard hardware, requires gr-iio" OFF) +option(ENABLE_AD9361 "Enable the use of AD9361 directo to FPGA hardware, requires gr-iio" OFF) # Performance analysis tools option(ENABLE_GPERFTOOLS "Enable linking to Gperftools libraries (tcmalloc and profiler)" OFF) diff --git a/cmake/Modules/Findlibiio.cmake b/cmake/Modules/Findlibiio.cmake new file mode 100644 index 000000000..a708d7062 --- /dev/null +++ b/cmake/Modules/Findlibiio.cmake @@ -0,0 +1,32 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_LIBIIO libiio) + +FIND_PATH( + LIBIIO_INCLUDE_DIRS + NAMES gnuradio/iio/api.h + HINTS $ENV{LIBIIO_DIR}/include + ${PC_LIBIIO_INCLUDEDIR} + PATHS ${CMAKE_INSTALL_PREFIX}/include + /usr/local/include + /usr/include +) + +FIND_LIBRARY( + LIBIIO_LIBRARIES + NAMES libiio.so + HINTS $ENV{LIBIIO_DIR}/lib + ${PC_LIBIIO_LIBDIR} + PATHS ${CMAKE_INSTALL_PREFIX}/lib + ${CMAKE_INSTALL_PREFIX}/lib64 + /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 + /usr/lib/x86_64-linux-gnu +) + +message("find libiio:") +message(${LIBIIO_LIBRARIES}) +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBIIO DEFAULT_MSG LIBIIO_LIBRARIES LIBIIO_INCLUDE_DIRS) +MARK_AS_ADVANCED(LIBIIO_LIBRARIES LIBIIO_INCLUDE_DIRS) diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt index 2fb8f4be8..ededc5dc7 100644 --- a/src/algorithms/signal_source/adapters/CMakeLists.txt +++ b/src/algorithms/signal_source/adapters/CMakeLists.txt @@ -35,6 +35,20 @@ if(ENABLE_PLUTOSDR OR ENABLE_FMCOMMS2) set(OPT_DRIVER_INCLUDE_DIRS ${OPT_DRIVER_INCLUDE_DIRS} ${IIO_INCLUDE_DIRS}) endif(ENABLE_PLUTOSDR OR ENABLE_FMCOMMS2) +if(ENABLE_AD9361) + find_package(libiio REQUIRED) + if(NOT LIBIIO_FOUND) + message(STATUS "gnuradio-iio not found, its installation is required.") + message(STATUS "Please build and install the following projects:") + message(STATUS " * libiio from https://github.com/analogdevicesinc/libiio") + message(STATUS " * libad9361-iio from https://github.com/analogdevicesinc/libad9361-iio") + message(STATUS " * gnuradio-iio from https://github.com/analogdevicesinc/gr-iio") + message(FATAL_ERROR "gnuradio-iio required for building gnss-sdr with this option enabled") + endif(NOT LIBIIO_FOUND) + set(OPT_LIBRARIES ${OPT_LIBRARIES} ${LIBIIO_LIBRARIES}) + set(OPT_DRIVER_INCLUDE_DIRS ${OPT_DRIVER_INCLUDE_DIRS} ${LIBIIO_INCLUDE_DIRS}) +endif(ENABLE_AD9361) + if(ENABLE_PLUTOSDR) ############################################## @@ -55,6 +69,16 @@ if(ENABLE_FMCOMMS2) endif(IIO_FOUND) endif(ENABLE_FMCOMMS2) +if(ENABLE_AD9361) + ############################################### + # AD9361 DIRECT TO FPGA Hardware + ############################################### + if(LIBIIO_FOUND) + set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} ad9361_fpga_signal_source.cc) + endif(LIBIIO_FOUND) +endif(ENABLE_AD9361) + + if(ENABLE_GN3S) ############################################## diff --git a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc new file mode 100644 index 000000000..3aaf74a79 --- /dev/null +++ b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.cc @@ -0,0 +1,299 @@ +/*! + * \file ad9361_fpga_signal_source.cc + * \brief signal source for Analog Devices front-end AD9361 connected directly to FPGA accelerators. + * This source implements only the AD9361 control. It is NOT compatible with conventional SDR acquisition and tracking blocks. + * Please use the fmcomms2 source if conventional SDR acquisition and tracking is selected in the configuration file. + * \author Javier Arribas, jarribas(at)cttc.es + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR 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, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#include "ad9361_fpga_signal_source.h" +#include "configuration_interface.h" +#include "GPS_L1_CA.h" +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + + +/* RX is input, TX is output */ +enum iodev { RX, TX }; + +/* common RX and TX streaming params */ +struct stream_cfg { + long long bw_hz; // Analog banwidth in Hz + long long fs_hz; // Baseband sample rate in Hz + long long lo_hz; // Local oscillator frequency in Hz + const char* rfport; // Port name +}; + + +using google::LogMessage; + + +/* static scratch mem for strings */ +static char tmpstr[64]; + +/* IIO structs required for streaming */ +static struct iio_context *ctx = NULL; +static struct iio_channel *rx0_i = NULL; +static struct iio_channel *rx0_q = NULL; + +/* check return value of attr_write function */ +static void errchk(int v, const char* what) { + if (v < 0) { fprintf(stderr, "Error %d writing to channel \"%s\"\nvalue may not be supported.\n", v, what); } +} + +/* write attribute: long long int */ +static void wr_ch_lli(struct iio_channel *chn, const char* what, long long val) +{ + errchk(iio_channel_attr_write_longlong(chn, what, val), what); +} + +/* write attribute: string */ +static void wr_ch_str(struct iio_channel *chn, const char* what, const char* str) +{ + errchk(iio_channel_attr_write(chn, what, str), what); +} + + +/* helper function generating channel names */ +static char* get_ch_name(const char* type, int id) +{ + snprintf(tmpstr, sizeof(tmpstr), "%s%d", type, id); + return tmpstr; +} + +/* returns ad9361 phy device */ +static struct iio_device* get_ad9361_phy(struct iio_context *ctx) +{ + struct iio_device *dev = iio_context_find_device(ctx, "ad9361-phy"); + return dev; +} + +/* finds AD9361 streaming IIO devices */ +static bool get_ad9361_stream_dev(struct iio_context *ctx, enum iodev d, struct iio_device **dev) +{ + switch (d) { + case TX: *dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); return *dev != NULL; + case RX: *dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); return *dev != NULL; + default: return false; + } +} + +/* finds AD9361 streaming IIO channels */ +static bool get_ad9361_stream_ch(struct iio_context *ctx, enum iodev d, struct iio_device *dev, int chid, struct iio_channel **chn) +{ + *chn = iio_device_find_channel(dev, get_ch_name("voltage", chid), d == TX); + if (!*chn) + *chn = iio_device_find_channel(dev, get_ch_name("altvoltage", chid), d == TX); + return *chn != NULL; +} + +/* finds AD9361 phy IIO configuration channel with id chid */ +static bool get_phy_chan(struct iio_context *ctx, enum iodev d, int chid, struct iio_channel **chn) +{ + switch (d) { + case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), false); return *chn != NULL; + case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("voltage", chid), true); return *chn != NULL; + default: return false; + } +} + +/* finds AD9361 local oscillator IIO configuration channels */ +static bool get_lo_chan(struct iio_context *ctx, enum iodev d, struct iio_channel **chn) +{ + switch (d) { + // LO chan is always output, i.e. true + case RX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 0), true); return *chn != NULL; + case TX: *chn = iio_device_find_channel(get_ad9361_phy(ctx), get_ch_name("altvoltage", 1), true); return *chn != NULL; + default: return false; + } +} + + +/* applies streaming configuration through IIO */ +bool cfg_ad9361_streaming_ch(struct iio_context *ctx, struct stream_cfg *cfg, enum iodev type, int chid) +{ + struct iio_channel *chn = NULL; + + // Configure phy and lo channels + printf("* Acquiring AD9361 phy channel %d\n", chid); + if (!get_phy_chan(ctx, type, chid, &chn)) { return false; } + wr_ch_str(chn, "rf_port_select", cfg->rfport); + wr_ch_lli(chn, "rf_bandwidth", cfg->bw_hz); + wr_ch_lli(chn, "sampling_frequency", cfg->fs_hz); + + // Configure LO channel + printf("* Acquiring AD9361 %s lo channel\n", type == TX ? "TX" : "RX"); + if (!get_lo_chan(ctx, type, &chn)) { return false; } + wr_ch_lli(chn, "frequency", cfg->lo_hz); + return true; +} + + +Ad9361FpgaSignalSource::Ad9361FpgaSignalSource(ConfigurationInterface* configuration, + std::string role, unsigned int in_stream, unsigned int out_stream, + boost::shared_ptr queue) : + role_(role), in_stream_(in_stream), out_stream_(out_stream), + queue_(queue) +{ + std::string default_item_type = "gr_complex"; + std::string default_dump_file = "./data/signal_source.dat"; + uri_ = configuration->property(role + ".device_address", std::string("192.168.2.1")); + freq_ = configuration->property(role + ".freq", GPS_L1_FREQ_HZ); + sample_rate_ = configuration->property(role + ".sampling_frequency", 2600000); + bandwidth_ = configuration->property(role + ".bandwidth", 2000000); + rx1_en_ = configuration->property(role + ".rx1_enable", true); + rx2_en_ = configuration->property(role + ".rx2_enable", false); + buffer_size_ = configuration->property(role + ".buffer_size", 0xA0000); + quadrature_ = configuration->property(role + ".quadrature", true); + rf_dc_ = configuration->property(role + ".rf_dc", true); + bb_dc_ = configuration->property(role + ".bb_dc", true); + gain_mode_rx1_ = configuration->property(role + ".gain_mode_rx1", std::string("manual")); + gain_mode_rx2_ = configuration->property(role + ".gain_mode_rx2", std::string("manual")); + rf_gain_rx1_ = configuration->property(role + ".gain_rx1", 64.0); + rf_gain_rx2_ = configuration->property(role + ".gain_rx2", 64.0); + rf_port_select_ = configuration->property(role + ".rf_port_select", std::string("A_BALANCED")); + filter_file_ = configuration->property(role + ".filter_file", std::string("")); + filter_auto_ = configuration->property(role + ".filter_auto", true); + item_type_ = configuration->property(role + ".item_type", default_item_type); + samples_ = configuration->property(role + ".samples", 0); + dump_ = configuration->property(role + ".dump", false); + dump_filename_ = configuration->property(role + ".dump_filename", default_dump_file); + + item_size_ = sizeof(gr_complex); + + std::cout << "device address: " << uri_ << std::endl; + std::cout << "LO frequency : " << freq_ << " Hz" << std::endl; + std::cout << "sample rate: " << sample_rate_ << " Hz" << std::endl; + + + // AD9361 Frontend IC device operation + + // Streaming devices + struct iio_device *rx; + + // RX stream config + // Stream configurations + struct stream_cfg rxcfg; + rxcfg.bw_hz = bandwidth_; // 2 MHz rf bandwidth + rxcfg.fs_hz = sample_rate_; // 2.5 MS/s rx sample rate + rxcfg.lo_hz = freq_; // 2.5 GHz rf frequency + rxcfg.rfport = rf_port_select_.c_str(); // port A (select for rf freq.) + + + std::cout<<"AD9361 Acquiring IIO context\n"; + ctx = iio_create_default_context(); + if (!ctx) + { + std::cout<<"No context\n"; + throw std::runtime_error("AD9361 IIO No context"); + } + + if (iio_context_get_devices_count(ctx) <= 0) + { + std::cout<<"No devices\n"; + throw std::runtime_error("AD9361 IIO No devices"); + } + + std::cout<<"* Acquiring AD9361 streaming devices\n"; + + if(!get_ad9361_stream_dev(ctx, RX, &rx)) + { + std::cout<<"No rx dev found\n"; + throw std::runtime_error("AD9361 IIO No rx dev found"); + }; + + std::cout<<"* Configuring AD9361 for streaming\n"; + if (!cfg_ad9361_streaming_ch(ctx, &rxcfg, RX, 0)) + { + std::cout<<"RX port 0 not found\n"; + throw std::runtime_error("AD9361 IIO RX port 0 not found"); + } + + std::cout<<"* Initializing AD9361 IIO streaming channels\n"; + if (!get_ad9361_stream_ch(ctx, RX, rx, 0, &rx0_i)) + { + std::cout<<"RX chan i not found\n"; + throw std::runtime_error("RX chan i not found"); + } + + if (!get_ad9361_stream_ch(ctx, RX, rx, 1, &rx0_q)) + { + std::cout<<"RX chan q not found\n"; + throw std::runtime_error("RX chan q not found"); + } + + std::cout<<"* Enabling IIO streaming channels\n"; + iio_channel_enable(rx0_i); + iio_channel_enable(rx0_q); + +} + + +Ad9361FpgaSignalSource::~Ad9361FpgaSignalSource() +{ + /* cleanup and exit */ + std::cout<<"* AD9361 Disabling streaming channels\n"; + if (rx0_i) { iio_channel_disable(rx0_i); } + if (rx0_q) { iio_channel_disable(rx0_q); } + + std::cout<<"* AD9361 Destroying context\n"; + if (ctx) { iio_context_destroy(ctx); } +} + + +void Ad9361FpgaSignalSource::connect(gr::top_block_sptr top_block) +{ + DLOG(INFO) << "AD9361 FPGA source nothing to connect"; +} + + +void Ad9361FpgaSignalSource::disconnect(gr::top_block_sptr top_block) +{ + DLOG(INFO) << "AD9361 FPGA source nothing to disconnect"; +} + + +gr::basic_block_sptr Ad9361FpgaSignalSource::get_left_block() +{ + LOG(WARNING) << "Trying to get signal source left block."; + return gr::basic_block_sptr(); +} + + +gr::basic_block_sptr Ad9361FpgaSignalSource::get_right_block() +{ + LOG(WARNING) << "Trying to get AD9361 FPGA signal source right block."; + return gr::basic_block_sptr(); +} diff --git a/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h new file mode 100644 index 000000000..ff6b796b7 --- /dev/null +++ b/src/algorithms/signal_source/adapters/ad9361_fpga_signal_source.h @@ -0,0 +1,109 @@ +/*! + * \file ad9361_fpga_signal_source.h + * \brief signal source for Analog Devices front-end AD9361 connected directly to FPGA accelerators. + * This source implements only the AD9361 control. It is NOT compatible with conventional SDR acquisition and tracking blocks. + * Please use the fmcomms2 source if conventional SDR acquisition and tracking is selected in the configuration file. + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2017 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR 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, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR 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 for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_AD9361_FPGA_SIGNAL_SOURCE_H_ +#define GNSS_SDR_AD9361_FPGA_SIGNAL_SOURCE_H_ + +#include "gnss_block_interface.h" + +#include +#include +#include + +class ConfigurationInterface; + +class Ad9361FpgaSignalSource: public GNSSBlockInterface +{ +public: + Ad9361FpgaSignalSource(ConfigurationInterface* configuration, + std::string role, unsigned int in_stream, + unsigned int out_stream, boost::shared_ptr queue); + + virtual ~Ad9361FpgaSignalSource(); + + inline std::string role() override + { + return role_; + } + + /*! + * \brief Returns "Ad9361_Fpga_Signal_Source" + */ + inline std::string implementation() override + { + return "Ad9361_Fpga_Signal_Source"; + } + + inline size_t item_size() override + { + return item_size_; + } + + void connect(gr::top_block_sptr top_block) override; + void disconnect(gr::top_block_sptr top_block) override; + gr::basic_block_sptr get_left_block() override; + gr::basic_block_sptr get_right_block() override; + +private: + std::string role_; + + // Front-end settings + std::string uri_;//device direction + unsigned long freq_; //frequency of local oscilator + unsigned long sample_rate_; + unsigned long bandwidth_; + unsigned long buffer_size_; //reception buffer + bool rx1_en_; + bool rx2_en_; + bool quadrature_; + bool rf_dc_; + bool bb_dc_; + std::string gain_mode_rx1_; + std::string gain_mode_rx2_; + double rf_gain_rx1_; + double rf_gain_rx2_; + std::string rf_port_select_; + std::string filter_file_; + bool filter_auto_; + + unsigned int in_stream_; + unsigned int out_stream_; + + std::string item_type_; + size_t item_size_; + long samples_; + bool dump_; + std::string dump_filename_; + + boost::shared_ptr queue_; +}; + +#endif /*GNSS_SDR_AD9361_FPGA_SIGNAL_SOURCE_H_*/ diff --git a/src/core/receiver/CMakeLists.txt b/src/core/receiver/CMakeLists.txt index 0e12422a6..c922cdf3d 100644 --- a/src/core/receiver/CMakeLists.txt +++ b/src/core/receiver/CMakeLists.txt @@ -90,6 +90,11 @@ if(ENABLE_FMCOMMS2) set(OPT_RECEIVER_INCLUDE_DIRS ${OPT_RECEIVER_INCLUDE_DIRS} ${IIO_INCLUDE_DIRS}) endif(ENABLE_FMCOMMS2) +if(ENABLE_AD9361) + add_definitions(-DAD9361_DRIVER=1) + set(OPT_RECEIVER_INCLUDE_DIRS ${OPT_RECEIVER_INCLUDE_DIRS} ${IIO_INCLUDE_DIRS}) +endif(ENABLE_AD9361) + if(${PC_GNURADIO_RUNTIME_VERSION} VERSION_GREATER "3.7.13" ) add_definitions( -DGR_GREATER_38=1 ) endif(${PC_GNURADIO_RUNTIME_VERSION} VERSION_GREATER "3.7.13" ) diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index 1ebab2595..e7d096fab 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -132,6 +132,10 @@ #include "fmcomms2_signal_source.h" #endif +#if AD9361_DRIVER +#include "ad9361_fpga_signal_source.h" +#endif + #if FLEXIBAND_DRIVER #include "flexiband_signal_source.h" #endif @@ -1080,6 +1084,15 @@ std::unique_ptr GNSSBlockFactory::GetBlock( } #endif +#if AD9361_DRIVER + else if (implementation.compare("Ad9361_Fpga_Signal_Source") == 0) + { + std::unique_ptr block_(new Ad9361FpgaSignalSource(configuration.get(), role, in_streams, + out_streams, queue)); + block = std::move(block_); + } +#endif + #if FLEXIBAND_DRIVER else if (implementation.compare("Flexiband_Signal_Source") == 0) {