From 6311530cb4a8072c640cb5d6b200d65cd37958c3 Mon Sep 17 00:00:00 2001 From: Javier Arribas Date: Sun, 14 Aug 2022 12:10:59 +0200 Subject: [PATCH] Adding new Ad936x custom signal source, initial commit, experimental --- .../signal_source/adapters/CMakeLists.txt | 6 + .../adapters/ad936x_custom_signal_source.cc | 325 +++++ .../adapters/ad936x_custom_signal_source.h | 118 ++ .../gnuradio_blocks/CMakeLists.txt | 7 + .../gnuradio_blocks/ad936x_iio_source.cc | 262 +++- .../gnuradio_blocks/ad936x_iio_source.h | 115 +- .../signal_source/libs/CMakeLists.txt | 18 +- .../signal_source/libs/ad936x_iio_custom.cc | 1195 +++++++++++++++++ .../signal_source/libs/ad936x_iio_custom.h | 144 ++ .../signal_source/libs/ad936x_iio_samples.cc | 26 + .../signal_source/libs/ad936x_iio_samples.h | 41 + .../signal_source/libs/pps_samplestamp.h | 20 + src/algorithms/signal_source/libs/ppstcprx.cc | 154 +++ src/algorithms/signal_source/libs/ppstcprx.h | 34 + src/core/receiver/concurrent_queue.h | 12 + src/core/receiver/gnss_block_factory.cc | 7 + 16 files changed, 2405 insertions(+), 79 deletions(-) create mode 100644 src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc create mode 100644 src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_custom.cc create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_custom.h create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_samples.cc create mode 100644 src/algorithms/signal_source/libs/ad936x_iio_samples.h create mode 100644 src/algorithms/signal_source/libs/pps_samplestamp.h create mode 100644 src/algorithms/signal_source/libs/ppstcprx.cc create mode 100644 src/algorithms/signal_source/libs/ppstcprx.h diff --git a/src/algorithms/signal_source/adapters/CMakeLists.txt b/src/algorithms/signal_source/adapters/CMakeLists.txt index e99748358..40e72049c 100644 --- a/src/algorithms/signal_source/adapters/CMakeLists.txt +++ b/src/algorithms/signal_source/adapters/CMakeLists.txt @@ -21,6 +21,12 @@ if(ENABLE_PLUTOSDR) ############################################## set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} plutosdr_signal_source.cc) set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} plutosdr_signal_source.h) + + ############################################## + # CUSTOM AD936X IIO SOURCE + ############################################## + set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} ad936x_custom_signal_source.cc) + set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} ad936x_custom_signal_source.h) endif() diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc new file mode 100644 index 000000000..91f11b410 --- /dev/null +++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.cc @@ -0,0 +1,325 @@ +/*! + * \file ad936x_custom_signal_source.cc + * \brief A direct IIO custom front-end gnss-sdr signal source for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#include "ad936x_custom_signal_source.h" +#include "configuration_interface.h" +#include "gnss_frequencies.h" +#include "gnss_sdr_string_literals.h" +#include "gnss_sdr_valve.h" +#include +#include +#include +#include + +using namespace std::string_literals; + +Ad936xCustomSignalSource::Ad936xCustomSignalSource(const ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_stream, + unsigned int out_stream, + Concurrent_Queue* queue __attribute__((unused))) + : SignalSourceBase(configuration, role, "Ad936x_Custom_Signal_Source"s), + in_stream_(in_stream), + out_stream_(out_stream), + item_type_(configuration->property(role + ".item_type", std::string("gr_complex"))), + samples_(configuration->property(role + ".samples", int64_t(0))), + dump_(configuration->property(role + ".dump", false)), + dump_filename_(configuration->property(role + ".dump_filename", std::string("./data/signal_source.dat"))), + pluto_uri_(configuration->property(role + ".pluto_uri", std::string("local"))), + board_type_(configuration->property(role + ".board_type", std::string("single_ad9361"))), + sample_rate_(configuration->property(role + ".sampling_frequency", 4.0e6)), + bandwidth_(configuration->property(role + ".bandwidth", configuration->property(role + ".sampling_frequency", 4.0e6) / 1.1)), + freq_(configuration->property(role + ".freq", FREQ1)), + freq_2ch(configuration->property(role + ".freq_2ch", FREQ1)), + rf_port_select_(configuration->property(role + ".rf_port_select", std::string("A_BALANCED"))), + rf_filter(configuration->property(role + ".rf_filter", std::string("none"))), + gain_mode_rx0_(configuration->property(role + ".gain_mode_rx0", std::string("slow_attack"))), + gain_mode_rx1_(configuration->property(role + ".gain_mode_rx1", std::string("slow_attack"))), + rf_gain_rx0_(configuration->property(role + ".gain_rx0", 40.0)), + rf_gain_rx1_(configuration->property(role + ".gain_rx1", 40.0)), + enable_ch0(configuration->property(role + ".enable_ch0", true)), + enable_ch1(configuration->property(role + ".enable_ch1", false)), + PPS_mode_(configuration->property(role + ".PPS_mode", false)), + fe_ip_(configuration->property(role + ".fe_ip", std::string("192.168.2.1"))), + fe_ctlport_(configuration->property(role + ".fe_ctlport", int32_t(10000))), + ssize_(configuration->property(role + ".ssize", int32_t(16))), + bshift_(configuration->property(role + ".bshift", int64_t(0))), + spattern_(configuration->property(role + ".spattern", false)), + inverted_spectrum_ch0_(configuration->property(role + ".inverted_spectrum_ch0", false)), + inverted_spectrum_ch1_(configuration->property(role + ".inverted_spectrum_ch1", false)) + + +{ + if (item_type_ == "gr_complex") + { + item_size_ = sizeof(gr_complex); + // 1. Make the driver instance + bool customsamplesize = false; + if (ssize_ != 16 or spattern_ == true) customsamplesize = true; + + ad936x_iio_source = ad936x_iio_make_source_sptr( + pluto_uri_, + board_type_, + bandwidth_, + sample_rate_, + freq_, + rf_port_select_, + rf_filter, + gain_mode_rx0_, + gain_mode_rx1_, + rf_gain_rx0_, + rf_gain_rx1_, + enable_ch0, + enable_ch1, + freq_2ch, + PPS_mode_, + customsamplesize, + fe_ip_, + fe_ctlport_, + ssize_, + bshift_, + spattern_); + + n_channels = 1; + if (enable_ch0 == true and enable_ch1 == true) + { + n_channels = 2; + } + + for (int n = 0; n < n_channels; n++) + { + if (ssize_ == 16) + { + gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make()); + } + else if (ssize_ == 8) + { + gr_interleaved_char_to_complex_.push_back(gr::blocks::interleaved_char_to_complex::make()); + } + else if (ssize_ == 4) + { + gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false)); + unpack_byte_fourbits.push_back(make_unpack_byte_4bit_samples()); + } + else if (ssize_ == 2) + { + gr_interleaved_short_to_complex_.push_back(gr::blocks::interleaved_short_to_complex::make(false, false)); + unpack_byte_twobits.push_back(make_unpack_byte_2bit_cpx_samples()); + } + } + } + else + { + LOG(ERROR) << item_type_ << " unrecognized item type"; + exit(1); + } + + + if (dump_) + { + for (int n = 0; n < n_channels; n++) + { + DLOG(INFO) << "Dumping output into file " << (dump_filename_ + "c_h" + std::to_string(n) + ".bin"); + sink_.emplace_back(gr::blocks::file_sink::make(item_size_, (dump_filename_ + "_ch" + std::to_string(n) + ".bin").c_str())); + } + } + + if (in_stream_ > 0) + { + LOG(ERROR) << "A signal source does not have an input stream"; + } + if (out_stream_ > 1) + { + LOG(ERROR) << "This implementation only supports one output stream"; + } +} + + +void Ad936xCustomSignalSource::connect(gr::top_block_sptr top_block) +{ + for (int n = 0; n < n_channels; n++) + { + if (ssize_ == 16) + { + top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 8) + { + top_block->connect(ad936x_iio_source, n, gr_interleaved_char_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 4) + { + top_block->connect(ad936x_iio_source, n, unpack_byte_fourbits.at(n), 0); + top_block->connect(unpack_byte_fourbits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 2) + { + top_block->connect(ad936x_iio_source, n, unpack_byte_twobits.at(n), 0); + top_block->connect(unpack_byte_twobits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else + { + top_block->connect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + } +} + + +void Ad936xCustomSignalSource::disconnect(gr::top_block_sptr top_block) +{ + for (int n = 0; n < n_channels; n++) + { + if (ssize_ == 16) + { + top_block->disconnect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n; + if (dump_) + { + top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 8) + { + top_block->disconnect(ad936x_iio_source, n, gr_interleaved_char_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_char_to_complex_ for channel " << n; + if (dump_) + { + top_block->disconnect(gr_interleaved_char_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 4) + { + top_block->disconnect(ad936x_iio_source, n, unpack_byte_fourbits.at(n), 0); + top_block->disconnect(unpack_byte_fourbits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n; + if (dump_) + { + top_block->connect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else if (ssize_ == 2) + { + top_block->disconnect(ad936x_iio_source, n, unpack_byte_twobits.at(n), 0); + top_block->disconnect(unpack_byte_twobits.at(n), 0, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to unpack_byte_fourbits for channel " << n; + if (dump_) + { + top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + else + { + top_block->disconnect(ad936x_iio_source, n, gr_interleaved_short_to_complex_.at(n), 0); + DLOG(INFO) << "connected ad936x_iio_source source to gr_interleaved_short_to_complex for channel " << n; + if (dump_) + { + top_block->disconnect(gr_interleaved_short_to_complex_.at(n), 0, sink_.at(n), 0); + DLOG(INFO) << "connected source to file sink"; + } + } + } +} + + +gr::basic_block_sptr Ad936xCustomSignalSource::get_left_block() +{ + LOG(WARNING) << "Trying to get signal source left block."; + return {}; +} + + +gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block() +{ + if (ssize_ == 16) + { + return gr_interleaved_short_to_complex_.at(0); + } + else if (ssize_ == 8) + { + return gr_interleaved_char_to_complex_.at(0); + } + else if (ssize_ == 4) + { + return gr_interleaved_short_to_complex_.at(0); + } + else if (ssize_ == 2) + { + return gr_interleaved_short_to_complex_.at(0); + } + else + { + return gr_interleaved_short_to_complex_.at(0); + } +} + +gr::basic_block_sptr Ad936xCustomSignalSource::get_right_block(int RF_channel) +{ + if (ssize_ == 16) + { + return gr_interleaved_short_to_complex_.at(RF_channel); + } + else if (ssize_ == 8) + { + return gr_interleaved_char_to_complex_.at(RF_channel); + } + else if (ssize_ == 4) + { + return gr_interleaved_short_to_complex_.at(RF_channel); + } + else if (ssize_ == 2) + { + return gr_interleaved_short_to_complex_.at(RF_channel); + } + else + { + return gr_interleaved_short_to_complex_.at(RF_channel); + } +} diff --git a/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h new file mode 100644 index 000000000..cc9abb82b --- /dev/null +++ b/src/algorithms/signal_source/adapters/ad936x_custom_signal_source.h @@ -0,0 +1,118 @@ +/*! + * \file ad936x_custom_signal_source.h + * \brief A direct IIO custom front-end gnss-sdr signal source for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H +#define GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H + +#include "ad936x_iio_source.h" +#include "concurrent_queue.h" +#include "conjugate_cc.h" +#include "signal_source_base.h" +#include "unpack_byte_2bit_cpx_samples.h" +#include "unpack_byte_4bit_samples.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_adapters + * \{ */ + + +class ConfigurationInterface; + +/*! + * \brief This class instantiates the Ad936xCustom gnuradio signal source. + * It has support also for a customized Ad936xCustom firmware and signal source to support PPS samplestamp reading. + */ +class Ad936xCustomSignalSource : public SignalSourceBase +{ +public: + Ad936xCustomSignalSource(const ConfigurationInterface* configuration, + const std::string& role, unsigned int in_stream, + unsigned int out_stream, Concurrent_Queue* queue); + + ~Ad936xCustomSignalSource() = default; + + 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; + gr::basic_block_sptr get_right_block(int RF_channel) override; + +private: + unsigned int in_stream_; + unsigned int out_stream_; + gr::block_sptr ad936x_iio_source; + std::vector sink_; + std::vector filename_vec_; + + std::vector gr_interleaved_short_to_complex_; + std::vector gr_interleaved_char_to_complex_; + std::vector unpack_byte_fourbits; + std::vector unpack_byte_twobits; + + + std::string item_type_; + size_t item_size_; + int64_t samples_; + bool dump_; + std::string dump_filename_; + + // Front-end settings + std::string pluto_uri_; + std::string board_type_; + long long sample_rate_; + long long bandwidth_; + long long freq_; + long long freq_2ch; + std::string rf_port_select_; + std::string rf_filter; + std::string gain_mode_rx0_; + std::string gain_mode_rx1_; + double rf_gain_rx0_; + double rf_gain_rx1_; + bool enable_ch0; + bool enable_ch1; + bool PPS_mode_; + std::string fe_ip_; + int fe_ctlport_; + int ssize_; + int bshift_; + bool spattern_; + bool inverted_spectrum_ch0_; + bool inverted_spectrum_ch1_; + + + int n_channels; +}; + + +/** \} */ +/** \} */ +#endif // GNSS_SDR_Ad936xCustom_SIGNAL_SOURCE_H diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt index c1198c08b..b09f94cca 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt +++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt @@ -17,6 +17,13 @@ if(ENABLE_AD936X_SDR) set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} gr_complex_ip_packet_source.h) endif() + +if(ENABLE_PLUTOSDR) + set(OPT_DRIVER_SOURCES ${OPT_DRIVER_SOURCES} ad936x_iio_source.cc) + set(OPT_DRIVER_HEADERS ${OPT_DRIVER_HEADERS} ad936x_iio_source.h) +endif() + + set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES fifo_reader.cc unpack_byte_2bit_samples.cc diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc index efee6ad49..f19a7e0a3 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.cc @@ -1,16 +1,14 @@ /*! * \file ad936x_iio_source.cc - * - * \brief Unpacks capture files in the LabSat 2 (ls2), LabSat 3 (ls3), or LabSat - * 3 Wideband (LS3W) formats. - * \author Javier Arribas jarribas (at) cttc.es + * \brief A direct IIO custom front-end gnss-sdr signal gnuradio block for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es * * ----------------------------------------------------------------------------- * * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. * This file is part of GNSS-SDR. * - * Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors) + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) * SPDX-License-Identifier: GPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -19,12 +17,13 @@ #include "ad936x_iio_source.h" #include "INIReader.h" +#include "ad936x_iio_samples.h" #include "command_event.h" #include "gnss_sdr_make_unique.h" +#include "pps_samplestamp.h" #include #include #include -#include #include #include #include @@ -33,64 +32,245 @@ #include -ad936x_iio_source_sptr ad936x_iio_make_source_sptr(Concurrent_Queue *queue, - std::string pluto_device_uri, - std::string board_type, +ad936x_iio_source_sptr ad936x_iio_make_source_sptr( + std::string pluto_uri_, + std::string board_type_, long long bandwidth_, long long sample_rate_, - std::vector ch_list, - std::vector ch_gain_mode, - std::vector ch_gain_db, - std::vector ch_freq_hz, - int ch_sample_size, - int ch_sample_bits_shift) + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch, + bool ppsmode_, + bool customsamplesize_, + std::string fe_ip_, + int fe_ctlport_, + int ssize_, + int bshift_, + bool spattern_) { - return ad936x_iio_source_sptr(new ad936x_iio_source(*queue, - pluto_device_uri, - board_type, + return ad936x_iio_source_sptr(new ad936x_iio_source( + pluto_uri_, + board_type_, bandwidth_, sample_rate_, - ch_list, - ch_gain_mode, - ch_gain_db, - ch_freq_hz, - ch_sample_size, - ch_sample_bits_shift)); + freq_, + rf_port_select_, + rf_filter, + gain_mode_rx0_, + gain_mode_rx1_, + rf_gain_rx0_, + rf_gain_rx1_, + enable_ch0, + enable_ch1, + freq_2ch, + ppsmode_, + customsamplesize_, + fe_ip_, + fe_ctlport_, + ssize_, + bshift_, + spattern_)); } -ad936x_iio_source::ad936x_iio_source(Concurrent_Queue *queue, - std::string pluto_device_uri, - std::string board_type, +ad936x_iio_source::ad936x_iio_source( + std::string pluto_uri_, + std::string board_type_, long long bandwidth_, long long sample_rate_, - std::vector ch_list, - std::vector ch_gain_mode, - std::vector ch_gain_db, - std::vector ch_freq_hz, - int ch_sample_size, - int ch_sample_bits_shift) : gr::block("ad936x_iio_source", - gr::io_signature::make(0, 0, 0), - gr::io_signature::make(1, 3, sizeof(gr_complex))) + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch, + bool ppsmode_, + bool customsamplesize_, + std::string fe_ip_, + int fe_ctlport_, + int ssize_, + int bshift_, + bool spattern_) : gr::block("ad936x_iio_source", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(1, 4, sizeof(int16_t))) { -} + ad936x_custom = std::make_unique(0, 0); + try + { + if (ad936x_custom->initialize_device(pluto_uri_, board_type_) == true) + { + //configure channels + if (ad936x_custom->init_config_ad9361_rx(bandwidth_, + sample_rate_, + freq_, + rf_port_select_, + rf_filter, + gain_mode_rx0_, + gain_mode_rx1_, + rf_gain_rx0_, + rf_gain_rx1_, + enable_ch0, + enable_ch1, + freq_2ch) == true) + { + std::cout << "ad936x_iio_source HW configured OK!\n"; + //PPS FPGA Samplestamp information from TCP server + pps_rx = std::make_shared(); + ppsqueue = std::shared_ptr>(new Concurrent_Queue()); + + pps_rx->set_pps_samplestamp_queue(ppsqueue); + ad936x_custom->set_pps_samplestamp_queue(ppsqueue); + + //start PPS RX thread + if (ppsmode_ == true or customsamplesize_ == true) + { + pps_rx_thread = std::thread(&pps_tcp_rx::receive_pps, pps_rx, fe_ip_, fe_ctlport_); + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + //configure custom FPGA options + switch (ssize_) + { + case 16: + { + std::cout << "FPGA sample size set to 16 bits per sample.\n"; + if (pps_rx->send_cmd("ssize=16\n") == false) std::cout << "cmd send error!\n"; + break; + } + case 8: + { + std::cout << "FPGA sample size set to 8 bits per sample.\n"; + if (pps_rx->send_cmd("ssize=8\n") == false) std::cout << "cmd send error!\n"; + break; + } + case 4: + { + std::cout << "FPGA sample size set to 4 bits per sample.\n"; + if (pps_rx->send_cmd("ssize=4\n") == false) std::cout << "cmd send error!\n"; + break; + } + case 2: + { + std::cout << "FPGA sample size set to 2 bits per sample.\n"; + if (pps_rx->send_cmd("ssize=2\n") == false) std::cout << "cmd send error!\n"; + break; + } + default: + { + std::cout << "WARNING: Unsupported ssize. FPGA sample size set to 16 bits per sample.\n"; + if (pps_rx->send_cmd("ssize=16") == false) std::cout << "cmd send error!\n"; + } + } + + if (bshift_ >= 0 and bshift_ <= 14) + { + std::cout << "FPGA sample bits shift left set to " + std::to_string(bshift_) + " positions.\n"; + if (pps_rx->send_cmd("bshift=" + std::to_string(bshift_) + "\n") == false) std::cout << "cmd send error!\n"; + } + else + { + std::cout << "WARNING: Unsupported bshift. FPGA sample bits shift left set to 0.\n"; + if (pps_rx->send_cmd("bshift=0\n") == false) std::cout << "cmd send error!\n"; + } + + if (spattern_ == true) + { + std::cout << "FPGA debug sample pattern is active!.\n"; + if (pps_rx->send_cmd("spattern=1\n") == false) std::cout << "cmd send error!\n"; + } + else + { + std::cout << "FPGA debug sample pattern disabled.\n"; + if (pps_rx->send_cmd("spattern=0\n") == false) std::cout << "cmd send error!\n"; + } + } + else + { + std::cout << "PPS mode NOT enabled, not configuring PlutoSDR custom timestamping FPGA IP.\n"; + } + } + else + { + std::cerr << "ad936x_iio_source IIO initialization error." << std::endl; + exit(1); + } + } + else + { + std::cerr << "ad936x_iio_source IIO initialization error." << std::endl; + exit(1); + } + } + catch (std::exception const &ex) + { + std::cerr << "STD exception: " << ex.what() << std::endl; + exit(1); + } + catch (...) + { + std::cerr << "Unexpected catch" << std::endl; + exit(1); + } + + //set_min_noutput_items(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2); + set_min_output_buffer(IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2); + //std::cout << "max_output_buffer " << min_output_buffer(0) << " min_noutput_items: " << min_noutput_items() << "\n"; +} ad936x_iio_source::~ad936x_iio_source() { + // Terminate PPS thread + if (pps_rx_thread.joinable()) + { + pthread_t id = pps_rx_thread.native_handle(); + pps_rx_thread.detach(); + pthread_cancel(id); + } } +bool ad936x_iio_source::start() +{ + return ad936x_custom->start_sample_rx(false); +} + +bool ad936x_iio_source::stop() +{ + ad936x_custom->stop_record(); + return true; +} + int ad936x_iio_source::general_work(int noutput_items, __attribute__((unused)) gr_vector_int &ninput_items, __attribute__((unused)) gr_vector_const_void_star &input_items, gr_vector_void_star &output_items) { - std::vector out; - for (auto &output_item : output_items) + std::shared_ptr current_buffer; + ad936x_iio_samples *current_samples; + ad936x_custom->pop_sample_buffer(current_buffer); + current_samples = current_buffer.get(); + + //I and Q samples are interleaved in buffer: IQIQIQ... + + for (size_t n = 0; n < ad936x_custom->n_channels; n++) { - out.push_back(reinterpret_cast(output_item)); + if (output_items.size() > n) // check if the output channel is connected + { + memcpy(reinterpret_cast(output_items[n]), reinterpret_cast(current_samples->buffer[n]), current_samples->n_bytes[n]); + produce(n, current_samples->n_samples[n]); + } } - std::cout << "Warning!!\n"; - return 0; + + ad936x_custom->push_sample_buffer(current_buffer); + return this->WORK_CALLED_PRODUCE; } diff --git a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h index aec4a3c56..497146277 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h +++ b/src/algorithms/signal_source/gnuradio_blocks/ad936x_iio_source.h @@ -1,8 +1,7 @@ /*! * \file ad936x_iio_source.h - * - * \brief signal source to receive samples from the AD936x FE family over libiio, including special custom functionalities in FPGA firmware. - * \author Javier Arribas jarribas (at) cttc.es + * \brief A direct IIO custom front-end gnss-sdr signal gnuradio block for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es * * ----------------------------------------------------------------------------- * @@ -15,19 +14,22 @@ * ----------------------------------------------------------------------------- */ -#ifndef GNSS_SDR_AD9361_IIO_SOURCE_H -#define GNSS_SDR_AD9361_IIO_SOURCE_H +#ifndef GNSS_SDR_AD936X_IIO_SOURCE_H +#define GNSS_SDR_AD936X_IIO_SOURCE_H + +#include "ad936x_iio_custom.h" #include "concurrent_queue.h" #include "gnss_block_interface.h" +#include "ppstcprx.h" #include -#include #include -#include //multichip sync and high level functions #include #include #include +#include #include +#include #include /** \addtogroup Signal_Source @@ -41,17 +43,27 @@ class ad936x_iio_source; using ad936x_iio_source_sptr = gnss_shared_ptr; ad936x_iio_source_sptr ad936x_iio_make_source_sptr( - Concurrent_Queue *queue, - std::string pluto_device_uri, - std::string board_type, + std::string pluto_uri_, + std::string board_type_, long long bandwidth_, long long sample_rate_, - std::vector ch_list, - std::vector ch_gain_mode, - std::vector ch_gain_db, - std::vector ch_freq_hz, - int ch_sample_size, - int ch_sample_bits_shift); + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch, + bool ppsmode_, + bool customsamplesize_, + std::string fe_ip_, + int fe_ctlport_, + int ssize_, + int bshift_, + bool spattern_); /*! * \brief This class implements conversion between Labsat 2, 3 and 3 Wideband @@ -62,6 +74,11 @@ class ad936x_iio_source : public gr::block public: ~ad936x_iio_source(); + //! start the sample transmission + bool start(); + //! stop the sample transmission + bool stop(); + int general_work(int noutput_items, gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, @@ -69,32 +86,60 @@ public: private: friend ad936x_iio_source_sptr ad936x_iio_make_source_sptr( - Concurrent_Queue *queue, - std::string pluto_device_uri, - std::string board_type, + std::string pluto_uri_, + std::string board_type_, long long bandwidth_, long long sample_rate_, - std::vector ch_list, - std::vector ch_gain_mode, - std::vector ch_gain_db, - std::vector ch_freq_hz, - int ch_sample_size, - int ch_sample_bits_shift); + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch, + bool ppsmode_, + bool customsamplesize_, + std::string fe_ip_, + int fe_ctlport_, + int ssize_, + int bshift_, + bool spattern_); - ad936x_iio_source(Concurrent_Queue *queue, - std::string pluto_device_uri, - std::string board_type, + ad936x_iio_source( + std::string pluto_uri_, + std::string board_type_, long long bandwidth_, long long sample_rate_, - std::vector ch_list, - std::vector ch_gain_mode, - std::vector ch_gain_db, - std::vector ch_freq_hz, - int ch_sample_size, - int ch_sample_bits_shift); + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch, + bool ppsmode_, + bool customsamplesize_, + std::string fe_ip_, + int fe_ctlport_, + int ssize_, + int bshift_, + bool spattern_); + + std::thread pps_rx_thread; + + + std::unique_ptr ad936x_custom; + std::shared_ptr pps_rx; + std::shared_ptr> ppsqueue; }; /** \} */ /** \} */ -#endif // GNSS_SDR_AD9361_IIO_SOURCE_H +#endif // GNSS_SDR_AD936X_IIO_SOURCE_H diff --git a/src/algorithms/signal_source/libs/CMakeLists.txt b/src/algorithms/signal_source/libs/CMakeLists.txt index 34cb13e88..00555c424 100644 --- a/src/algorithms/signal_source/libs/CMakeLists.txt +++ b/src/algorithms/signal_source/libs/CMakeLists.txt @@ -8,8 +8,8 @@ set(OPT_SIGNAL_SOURCE_LIB_SOURCES "") set(OPT_SIGNAL_SOURCE_LIB_HEADERS "") if(ENABLE_FMCOMMS2 OR ENABLE_AD9361) - set(OPT_SIGNAL_SOURCE_LIB_SOURCES ad9361_manager.cc) - set(OPT_SIGNAL_SOURCE_LIB_HEADERS ad9361_manager.h) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad9361_manager.h) endif() if(ENABLE_FPGA OR ENABLE_AD9361) @@ -23,6 +23,18 @@ if(ENABLE_FPGA OR ENABLE_AD9361) set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_HEADERS} fpga_dma.h) endif() + +if(ENABLE_PLUTOSDR) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_samples.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_samples.h) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_custom.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ad936x_iio_custom.h) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} pps_samplestamp.h) + set(OPT_SIGNAL_SOURCE_LIB_SOURCES ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ppstcprx.cc) + set(OPT_SIGNAL_SOURCE_LIB_HEADERS ${OPT_SIGNAL_SOURCE_LIB_SOURCES} ppstcprx.h) +endif() + + set(SIGNAL_SOURCE_LIB_SOURCES rtl_tcp_commands.cc rtl_tcp_dongle_info.cc @@ -88,7 +100,7 @@ if(GNURADIO_USES_SPDLOG) ) endif() -if(ENABLE_FMCOMMS2 OR ENABLE_AD9361) +if(ENABLE_FMCOMMS2 OR ENABLE_AD9361 OR ENABLE_PLUTOSDR) target_link_libraries(signal_source_libs PUBLIC Iio::iio diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.cc b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc new file mode 100644 index 000000000..0076c27fc --- /dev/null +++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.cc @@ -0,0 +1,1195 @@ +/*! + * \file ad936x_iio_custom.cc + * \brief A direct IIO custom front-end driver for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ +#include "ad936x_iio_custom.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +ad936x_iio_custom::ad936x_iio_custom(int debug_level_, int log_level_) +{ + receive_samples = false; + fpga_overflow = false; + sample_rate_sps = 0; + ctx = NULL; + phy = NULL; + dds_dev = NULL; + stream_dev = NULL; + debug_level = debug_level_; + log_level = log_level_; + PPS_mode = false; + n_channels = 0; +} + +ad936x_iio_custom::~ad936x_iio_custom() +{ + //disable TX + if (phy != NULL) PlutoTxEnable(false); + + //Close device + if (ctx != NULL) iio_context_destroy(ctx); +} + + +void ad936x_iio_custom::set_gnsstime_queue(std::shared_ptr> queue) +{ + GnssTime_queue = std::move(queue); +} + +void ad936x_iio_custom::set_pps_samplestamp_queue(std::shared_ptr> queue) +{ + Pps_queue = std::move(queue); +} + +bool ad936x_iio_custom::initialize_device(std::string pluto_device_uri, std::string board_type) +{ + //Find devices + if (pluto_device_uri == "local") + { + struct iio_scan_context *tmp_ctx = iio_create_scan_context("usb", 0); + if (!tmp_ctx) + { + std::cout << "Unable to create scan context\n"; + return false; + } + + struct iio_context_info **info; + int ret = iio_scan_context_get_info_list(tmp_ctx, &info); + if (ret < 0) + { + iio_scan_context_destroy(tmp_ctx); + + std::cout << "Unable to scan for Pluto devices\n"; + return false; + } + + if (ret == 0) + { + iio_context_info_list_free(info); + iio_scan_context_destroy(tmp_ctx); + std::cout << " No Pluto device found\n "; + return false; + } + + if (ret > 1) + { + std::cout << "More than one Pluto found:\n"; + + for (unsigned int i = 0; i < (size_t)ret; i++) + { + printf("\t%d: %s [%s]\n", i, + iio_context_info_get_description(info[i]), + iio_context_info_get_uri(info[i])); + } + + std::cout << "We will use the first one.\n"; + } + + std::string uri(iio_context_info_get_uri(info[0])); + iio_context_info_list_free(info); + iio_scan_context_destroy(tmp_ctx); + + ctx = iio_create_context_from_uri(uri.c_str()); + } + else + { + ctx = iio_create_context_from_uri(pluto_device_uri.c_str()); + } + + if (ctx == NULL) + { + std::cout << "Unable to create context from uri: " << pluto_device_uri << std::endl; + return false; + } + + + phy = iio_context_find_device(ctx, "ad9361-phy"); + + if (phy == NULL) + { + std::cout << "Unable to find ad9361-phy device from uri: " << pluto_device_uri << std::endl; + return false; + } + + if (board_type.compare("fmcomms5") == 0) + { + stream_dev = iio_context_find_device(ctx, "cf-ad9361-A"); //first ad9361 in FMCOMMS5 + if (stream_dev == NULL) + { + std::cout << "Unable to find cf-ad9361-A device from uri: " << pluto_device_uri << std::endl; + return false; + }; + } + else + { + stream_dev = iio_context_find_device(ctx, "cf-ad9361-lpc"); //regular AD9361 stream device in single AD9361 boards + if (stream_dev == NULL) + { + std::cout << "Unable to find cf-ad9361-lpc device from uri: " << pluto_device_uri << std::endl; + return false; + }; + dds_dev = iio_context_find_device(ctx, "cf-ad9361-dds-core-lpc"); //DDS core for LO oscillator (external transverter operation) + if (stream_dev == NULL) + { + std::cout << "Warning: Unable to find cf-ad9361-dds-core-lpc device from uri: " << pluto_device_uri << std::endl; + }; + } + + return true; +} + + +void ad936x_iio_custom::configure_params(struct iio_device *phy, + const std::vector ¶ms) +{ + for (std::vector::const_iterator it = params.begin(); + it != params.end(); ++it) + { + struct iio_channel *chn = NULL; + const char *attr = NULL; + size_t pos; + int ret; + + pos = it->find('='); + if (pos == std::string::npos) + { + std::cerr << "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(phy, + key.c_str(), &chn, &attr); + if (ret) + { + std::cerr << "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(phy, attr)) + ret = iio_device_attr_write(phy, attr, val.c_str()); + else + ret = iio_device_debug_attr_write(phy, + attr, val.c_str()); + if (ret < 0) + { + std::cerr << "Unable to write attribute " << key + << ": " << ret << std::endl; + } + } +} + + +void ad936x_iio_custom::set_params_rx(struct iio_device *phy_device, + unsigned long long frequency, + unsigned long samplerate, unsigned long bandwidth, + bool quadrature, bool rfdc, bool bbdc, + std::string gain1, double gain1_value, + std::string gain2, double gain2_value, + std::string port_select) +{ + std::vector params; + + params.push_back("out_altvoltage0_RX_LO_frequency=" + + std::to_string(frequency)); + std::cout << params.back() << "\n"; + params.push_back("in_voltage_sampling_frequency=" + + std::to_string(samplerate)); + std::cout << params.back() << "\n"; + params.push_back("in_voltage_rf_bandwidth=" + + std::to_string(bandwidth)); + std::cout << params.back() << "\n"; + params.push_back("in_voltage_quadrature_tracking_en=" + + std::to_string(quadrature)); + params.push_back("in_voltage_rf_dc_offset_tracking_en=" + + std::to_string(rfdc)); + params.push_back("in_voltage_bb_dc_offset_tracking_en=" + + std::to_string(bbdc)); + params.push_back("in_voltage0_gain_control_mode=" + + gain1); + params.push_back("in_voltage0_hardwaregain=" + + std::to_string(gain1_value)); + params.push_back("in_voltage1_gain_control_mode=" + + gain2); + params.push_back("in_voltage1_hardwaregain=" + + std::to_string(gain2_value)); + params.push_back("in_voltage0_rf_port_select=" + + port_select); + + configure_params(phy_device, params); +} + + +bool ad936x_iio_custom::config_ad9361_dds(uint64_t freq_rf_tx_hz_, + double tx_attenuation_db_, + int64_t freq_dds_tx_hz_, + double scale_dds_, + double phase_dds_deg_) +{ + // TX stream config + std::cout << "Start of AD9361 TX Oscillator DDS configuration\n"; + + std::cout << "* Configuring AD9361 for streaming TX\n"; + + // ENABLE DDS on TX1 + // Configure LO channel + std::vector params_phy; + + params_phy.push_back("out_altvoltage1_TX_LO_frequency=" + + std::to_string(freq_rf_tx_hz_)); + + params_phy.push_back("out_voltage0_hardwaregain=" + + std::to_string(-tx_attenuation_db_)); + + //disable the other TX + params_phy.push_back("out_voltage1_hardwaregain=" + + std::to_string(-tx_attenuation_db_)); + + + configure_params(phy, params_phy); + + std::vector params_dds; + + //DDS TX CH1 I (tone #1) + params_dds.push_back("out_altvoltage0_TX1_I_F1_frequency=" + + std::to_string(freq_dds_tx_hz_)); + params_dds.push_back("out_altvoltage0_TX1_I_F1_phase=" + + std::to_string(phase_dds_deg_ * 1000.0)); + params_dds.push_back("out_altvoltage0_TX1_I_F1_scale=" + + std::to_string(scale_dds_)); + params_dds.push_back("out_altvoltage0_TX1_I_F1_raw=1"); + //DDS TX CH1 Q (tone #1) + params_dds.push_back("out_altvoltage2_TX1_Q_F1_frequency=" + + std::to_string(freq_dds_tx_hz_)); + params_dds.push_back("out_altvoltage2_TX1_Q_F1_phase=" + + std::to_string(phase_dds_deg_ * 1000.0 + 270000.0)); + params_dds.push_back("out_altvoltage2_TX1_Q_F1_scale=" + + std::to_string(scale_dds_)); + params_dds.push_back("out_altvoltage2_TX1_Q_F1_raw=1"); + + //DDS TX CH1 I (tone #1) + // params_dds.push_back("out_altvoltage4_TX2_I_F1_frequency=" + + // std::to_string(freq_dds_tx_hz_)); + // params_dds.push_back("out_altvoltage4_TX2_I_F1_phase=" + + // std::to_string(phase_dds_deg_ * 1000.0)); + // params_dds.push_back("out_altvoltage4_TX2_I_F1_scale=" + + // std::to_string(scale_dds_)); + // params_dds.push_back("out_altvoltage4_TX2_I_F1_raw=1"); + // //DDS TX CH1 Q (tone #1) + // params_dds.push_back("out_altvoltage6_TX2_Q_F1_frequency=" + + // std::to_string(freq_dds_tx_hz_)); + // params_dds.push_back("out_altvoltage6_TX2_Q_F1_phase=" + + // std::to_string(phase_dds_deg_ * 1000.0 + 270000.0)); + // params_dds.push_back("out_altvoltage6_TX2_Q_F1_scale=" + + // std::to_string(scale_dds_)); + // params_dds.push_back("out_altvoltage6_TX2_Q_F1_raw=1"); + + configure_params(dds_dev, params_dds); + + return true; +} + + +bool ad936x_iio_custom::check_device() +{ + if (stream_dev != NULL) + { + return true; + } + else + { + return false; + } +} + +bool ad936x_iio_custom::get_iio_param(iio_device *dev, const std::string ¶m, std::string &value) +{ + struct iio_channel *chn = 0; + const char *attr = 0; + char valuestr[256]; + int ret; + ssize_t nchars; + + ret = iio_device_identify_filename(dev, param.c_str(), &chn, &attr); + + if (ret) + { + std::cerr << "DevicePlutoSDR::get_param: Parameter not recognized: " << param << std::endl; + return false; + } + + if (chn) + { + nchars = iio_channel_attr_read(chn, attr, valuestr, 256); + } + else if (iio_device_find_attr(dev, attr)) + { + nchars = iio_device_attr_read(dev, attr, valuestr, 256); + } + else + { + nchars = iio_device_debug_attr_read(dev, attr, valuestr, 256); + } + + if (nchars < 0) + { + std::cerr << "DevicePlutoSDR::get_param: Unable to read attribute " << param << ": " << nchars << std::endl; + return false; + } + else + { + value.assign(valuestr); + return true; + } +} + +bool ad936x_iio_custom::read_die_temp(double &temp_c) +{ + std::string temp_mC_str; + + if (get_iio_param(phy, "in_temp0_input", temp_mC_str)) + { + try + { + uint32_t temp_mC = boost::lexical_cast(temp_mC_str); + temp_c = static_cast(temp_mC) / 1000.0; + if (temp_c > 120) temp_c = -1; + return true; + } + catch (const boost::bad_lexical_cast &e) + { + std::cerr << "PlutoSDRDevice::getTemp: bad conversion to numeric" << std::endl; + return false; + } + } + else + { + return false; + } +} +bool ad936x_iio_custom::init_config_ad9361_rx(long long bandwidth_, + long long sample_rate_, + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch) + +{ + if (check_device() == false) return false; + + bool no_errors = true; + std::cout << "Configuring phy device parameters...\n"; + int ret; + if (rf_filter.compare("Disabled") == 0) + { + std::cout << "LNA Filter switch is disabled.\n"; + } + else if (rf_filter.compare("Auto") == 0) + { + std::cout << "Selecting LNA RF filter based on the selected RF frequency... \n"; + if (freq_ == 1575420000) + { + if (select_rf_filter("E1") == true) + { + std::cout << "LNA RF filter board switch set to E1\n"; + } + else + { + std::cout << "Problem setting LNA RF filter switch value\n"; + } + } + else + { + if (select_rf_filter("E5E6") == true) + { + std::cout << "LNA RF filter board switch set to E5E6\n"; + } + else + { + std::cout << "Problem setting LNA RF filter switch value\n"; + } + } + } + else + { + if (select_rf_filter(rf_filter) == true) + { + std::cout << "LNA RF filter board switch set to " << rf_filter << "\n"; + } + else + { + std::cout << "Problem setting LNA RF filter switch value\n"; + } + } + + std::vector params; + // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator) + params.push_back("out_altvoltage0_RX_LO_frequency=" + + std::to_string(freq_)); + + sample_rate_sps = sample_rate_; + + params.push_back("in_voltage_sampling_frequency=" + + std::to_string(sample_rate_)); + + params.push_back("out_voltage_sampling_frequency=" + + std::to_string(sample_rate_)); + + params.push_back("in_voltage_rf_bandwidth=" + + std::to_string(bandwidth_)); + + params.push_back("out_voltage_rf_bandwidth=" + + std::to_string(bandwidth_)); + + params.push_back("in_voltage_quadrature_tracking_en=1"); + params.push_back("in_voltage_rf_dc_offset_tracking_en=1"); + params.push_back("in_voltage_bb_dc_offset_tracking_en=1"); + + configure_params(phy, params); + + ret = iio_device_attr_write(phy, "trx_rate_governor", "nominal"); + if (ret < 0) + { + std::cerr << "Failed to set trx_rate_governor: " << ret << std::endl; + no_errors = false; + } + ret = iio_device_attr_write(phy, "ensm_mode", "fdd"); + if (ret < 0) + { + std::cerr << "Failed to set ensm_mode: " << ret << std::endl; + no_errors = false; + } + ret = iio_device_attr_write(phy, "calib_mode", "auto"); + if (ret < 0) + { + std::cerr << "Failed to set calib_mode: " << ret << std::endl; + no_errors = false; + } + + + std::cout << "no_errors: " << no_errors << "\n"; + + if (enable_ch1 == true and enable_ch0 == true and freq_ != freq_2ch) + { + std::cout << "Two channels enabled with different frequencies, enabling the external RF transverter board:\n"; + long long int delta_freq_hz = freq_2ch - freq_; + if (delta_freq_hz < 0) + { + std::cout << "Configuration problem: 2nd channel frequency is " << freq_2ch << " [Hz], must be higher than main channel (" << freq_ << " [Hz])\n"; + return false; + } + + std::cout << "Configuring DDS Local Oscillator generation. LO Freq. is " << delta_freq_hz << " [Hz]\n"; + PlutoTxEnable(true); + config_ad9361_dds(delta_freq_hz, + 30, + 100000, + 0.9, + 0); + std::cout << "Configuring DDS Local Oscillator generation DONE\n"; + } + else + { + PlutoTxEnable(false); //power down the TX LO to reduce interferences + } + + if (enable_ch0 == true) + { + n_channels++; + std::cerr << "* Get AD9361 Phy RX channel 0...\n"; + std::stringstream name; + name.str(""); + name << "voltage"; + name << 0; + struct iio_channel *phy_ch; + phy_ch = iio_device_find_channel(phy, name.str().c_str(), false); //false means RX + if (!phy_ch) + { + std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n"; + no_errors = false; + } + + ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str()); + if (ret < 0) + { + std::cerr << "Warning: rf_port_select write returned: " << ret << "\n"; + no_errors = false; + } + + ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_); + if (ret < 0) + { + std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n"; + no_errors = false; + } + + long long set_rf_bw; + ret = iio_channel_attr_read_longlong(phy_ch, "rf_bandwidth", &set_rf_bw); + if (ret < 0) + { + std::cerr << "Warning: rf_bandwidth read returned: " << ret << "\n"; + no_errors = false; + } + else + { + std::cerr << "Info: rf_bandwidth read returned: " << set_rf_bw << " Hz \n"; + } + + + if (setRXGain(0, gain_mode_rx0_, rf_gain_rx0_) == false) + { + std::cerr << "Info: setRXGain read returned false \n"; + no_errors = false; + } + } + + if (enable_ch1 == true) + { + n_channels++; + std::cerr << "* Get AD9361 Phy RX channel 1...\n"; + std::stringstream name; + name.str(""); + name << "voltage"; + name << 1; + struct iio_channel *phy_ch; + phy_ch = iio_device_find_channel(phy, name.str().c_str(), false); //false means RX + if (!phy_ch) + { + std::cerr << "Could not find AD9361 phy channel: " << name.str() << "\n"; + no_errors = false; + } + + ret = iio_channel_attr_write(phy_ch, "rf_port_select", rf_port_select_.c_str()); + if (ret < 0) + { + std::cerr << "Warning: rf_port_select write returned: " << ret << "\n"; + no_errors = false; + } + + ret = iio_channel_attr_write_longlong(phy_ch, "rf_bandwidth", bandwidth_); + if (ret < 0) + { + std::cerr << "Warning: rf_bandwidth write returned: " << ret << "\n"; + no_errors = false; + } + + if (setRXGain(1, gain_mode_rx1_, rf_gain_rx1_) == false) + { + std::cerr << "Info: setRXGain read returned false \n"; + no_errors = false; + } + } + + int set_filter_ret = ad9361_set_bb_rate_custom_filter_auto(phy, sample_rate_sps); + if (set_filter_ret != 0) + { + std::cout << "Warning: Unable to set AD936x RX filter parameters!\n"; + } + + //testing: set manual RX filter chain + // unsigned long RX_analog_bb_lpf_stop_hz = 1000000; + // unsigned long TX_analog_bb_lpf_stop_hz = 1000000; + // + // unsigned long FIR_lpf_passband_hz = 1000000; + // unsigned long FIR_lpf_stopband_hz = 1200000; + // int set_filter_ret = ad9361_set_bb_rate_custom_filter_manual(phy, + // sample_rate_sps, + // FIR_lpf_passband_hz, + // FIR_lpf_stopband_hz, + // RX_analog_bb_lpf_stop_hz, + // TX_analog_bb_lpf_stop_hz); + // if (set_filter_ret != 0) + // { + // std::cout << "Warning: Unable to set AD936x RX filter parameters!\n"; + // } + + std::cout << "AD936x Front-end configuration summary: \n"; + std::cout << "Baseband sampling frequency: " << sample_rate_sps << " [SPS]\n"; + std::cout << "RX chain gain: " << rf_gain_rx0_ << " [dB][only valid in manual mode]\n"; + std::cout << "RX chain gain mode: " << gain_mode_rx0_ << "\n"; + // std::cout << "Analog baseband LPF stop frequency: " << RX_analog_bb_lpf_stop_hz << " [Hz]\n"; + // std::cout << "Digital baseband LPF FIR passband frequency: " << FIR_lpf_passband_hz << " [Hz]\n"; + // std::cout << "Digital baseband LPF FIR stopband frequency: " << FIR_lpf_stopband_hz << " [Hz]\n"; + std::cout << "End of AD9361 RX configuration.\n"; + return no_errors; +} + +bool ad936x_iio_custom::set_rx_frequency(long long freq_hz) +{ + if (check_device() == false) return false; + // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator) + struct iio_channel *lo_ch; + + lo_ch = iio_device_find_channel(phy, "altvoltage0", true); + if (!lo_ch) + { + std::cerr << "Could not find AD9361 RX LO channel altvoltage0\n"; + return false; + } + int ret; + ret = iio_channel_attr_write_longlong(lo_ch, "frequency", freq_hz); + if (ret < 0) + { + std::cerr << "Warning: RX LO frequency write returned: " << ret << "\n"; + return false; + } + return true; +} + + +bool ad936x_iio_custom::get_rx_frequency(long long &freq_hz) +{ + if (check_device() == false) return false; + // Configure RX LO channel (NOTICE that altvoltage0 is the RX LO oscillator!, altvoltage1 is the TX oscillator) + struct iio_channel *lo_ch; + + lo_ch = iio_device_find_channel(phy, "altvoltage0", true); + if (!lo_ch) + { + std::cerr << "Could not find AD9361 RX LO channel altvoltage0\n"; + return false; + } + int ret; + ret = iio_channel_attr_read_longlong(lo_ch, "frequency", &freq_hz); + if (ret < 0) + { + std::cerr << "Warning: RX LO frequency read returned: " << ret << "\n"; + return false; + } + return true; +} + +bool ad936x_iio_custom::setRXGain(int ch_num, std::string gain_mode, double gain_dB) +{ + if (check_device() == false) return false; + std::vector params; + if (ch_num == 0) + { + params.clear(); + params.push_back("in_voltage0_gain_control_mode=" + gain_mode); + if (gain_mode == "manual") + { + params.push_back("in_voltage0_hardwaregain=" + std::to_string(gain_dB)); + } + configure_params(phy, params); + return true; + } + else if (ch_num == 1) + { + params.clear(); + params.push_back("in_voltage1_gain_control_mode=" + gain_mode); + if (gain_mode == "manual") + { + params.push_back("in_voltage1_hardwaregain=" + std::to_string(gain_dB)); + } + configure_params(phy, params); + return true; + } + else + { + return false; + } +} + +double ad936x_iio_custom::get_rx_gain(int ch_num) +{ + if (check_device() == false) return -1; + double gain_dB; //gain in dB + int ret = 0; + if (ch_num == 0) + { + ret = iio_device_attr_read_double(phy, "in_voltage0_hardwaregain", &gain_dB); + if (ret < 0) + { + std::cerr << "Failed to read in_voltage0_hardwaregain: " << ret << std::endl; + return -1.0; + } + } + else if (ch_num == 1) + { + ret = iio_device_attr_read_double(phy, "in_voltage1_hardwaregain", &gain_dB); + if (ret < 0) + { + std::cerr << "Failed to read in_voltage1_hardwaregain: " << ret << std::endl; + return -1.0; + } + } + else + { + return -1.0; + } + return gain_dB; +} + + +bool ad936x_iio_custom::calibrate(int ch, double bw_hz) +{ + if (check_device() == false) return false; + //todo + return true; +} + +void ad936x_iio_custom::monitor_thread_fn() +{ + uint32_t val; + int ret; + + /* Give the main thread a moment to start the DMA */ + sleep(1); + + /* Clear all status bits */ + ret = iio_device_reg_write(stream_dev, 0x80000088, 0x6); + if (ret) + { + fprintf(stderr, "Failed to clearn DMA status register: %s\n", + strerror(-ret)); + } + + while (receive_samples) + { + ret = iio_device_reg_read(stream_dev, 0x80000088, &val); + if (ret) + { + fprintf(stderr, "Failed to read status register: %s\n", + strerror(-ret)); + continue; + } + + // if (device_is_tx) { + // if (val & 1) + // fprintf(stderr, "Underflow detected\n"); + // } else { + if (val & 4) + { + std::cout << "WARNING: IIO status register reported overflow!\n"; + LOG(INFO) << "WARNING: IIO status register reported overflow!"; + } + + + /* Clear bits */ + if (val) + { + ret = iio_device_reg_write(stream_dev, 0x80000088, val); + if (ret) + fprintf(stderr, "Failed to clearn DMA status register: %s\n", + strerror(-ret)); + } + sleep(1); + } + return; +} + +void ad936x_iio_custom::stop_record() +{ + receive_samples = false; + + if (capture_time_thread.joinable() == true) + { + std::cout << "Joining sample cature thread...\n"; + capture_samples_thread.join(); + } + + if (capture_time_thread.joinable() == true) + { + std::cout << "Joining overflow monitor thread...\n"; + overflow_monitor_thread.join(); + } + + if (capture_time_thread.joinable() == true) + { + std::cout << "Joining time cature thread...\n"; + capture_time_thread.join(); + } +} + + +void ad936x_iio_custom::PlutoTxEnable(bool txon) +{ + if (check_device()) + { + int ret; + if (txon == false) + { + ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", true); //turn off TX LO + if (ret < 0) + { + std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl; + } + } + else + { + ret = iio_channel_attr_write_bool(iio_device_find_channel(phy, "altvoltage1", true), "powerdown", false); //turn on TX LO + if (ret < 0) + { + std::cerr << "Failed to write altvoltage1 powerdown: " << ret << std::endl; + } + } + } +} + +void ad936x_iio_custom::setPlutoGpo(int p) +{ + char pins[11]; + sprintf(pins, "0x27 0x%x0", p); //direct access to AD9361 registers... WARNING! + pins[9] = 0; + int ret; + //std::cout << "send: " << pins << " \n"; + if (check_device()) + { + ret = iio_device_debug_attr_write(phy, "direct_reg_access", pins); + if (ret < 0) + { + std::cerr << "Failed to write direct_reg_access: " << ret << std::endl; + } + } +} + + +bool ad936x_iio_custom::select_rf_filter(std::string rf_filter) +{ + // adi,gpo-manual-mode-enable Enables GPO manual mode, this will conflict with automatic ENSM slave and eLNA mode + // adi,gpo-manual-mode-enable-mask Enable bit mask, setting or clearing bits will change the level of the corresponding output. Bit0 → GPO, Bit1 → GPO1, Bit2 → GPO2, Bit3 → GP03 + // adi,gpo-manual-mode-enable + // adi,gpo-manual-mode-enable-mask does not work... + // some software use the direct_reg_access (see https://github.com/g4eml/Langstone/blob/master/LangstoneGUI.c) + + //since plutosdr fw 31: + // GPOs can be addressed individual 0..3 or altogether using 0xF as Identifier. + // + // SYNTAX: + // + // gpo_set + // Enable + // Value Function + // 0 Disable + // 1 Enable + // X Enable Mask if Identifier=0xF + + + if (check_device() == false) return false; + int plutoGpo = 0; + int ret; + ret = iio_device_debug_attr_write(phy, "adi,gpo-manual-mode-enable", "1"); + + if (ret < 0) + { + std::cerr << "Failed to write adi,gpo-manual-mode-enable: " << ret << std::endl; + return false; + } + + if (rf_filter.compare("E1") == 0) + { + //set gpio0 to switch L1 filter + // setPlutoGpo(plutoGpo); + ret = iio_device_debug_attr_write(phy, "gpo_set", "0 0"); + if (ret < 0) + { + std::cerr << "Failed to write gpo_set: " << ret << std::endl; + return false; + } + } + else if (rf_filter.compare("E5E6") == 0) + { + //set gpio0 to switch L5/L6 filter (GPO0) + // plutoGpo = plutoGpo | 0x10; + // setPlutoGpo(plutoGpo); //set the Pluto GPO Pin + ret = iio_device_debug_attr_write(phy, "gpo_set", "0 1"); + if (ret < 0) + { + std::cerr << "Failed to write gpo_set: " << ret << std::endl; + return false; + } + } + if (rf_filter.compare("none") == 0) + { + std::cout << "RF external filter not selected\n"; + } + + else + { + std::cout << "Unknown filter selected, switching to E1 filter...\n"; + ret = iio_device_debug_attr_write(phy, "gpo_set", "0 0"); + if (ret < 0) + { + std::cerr << "Failed to write gpo_set: " << ret << std::endl; + return false; + } + } + + return true; +} +void ad936x_iio_custom::get_PPS_timestamp() +{ + GnssTime tow; + PpsSamplestamp pps; + GnssTime_queue->clear(); + Pps_queue->clear(); + + std::cout << "Waiting for uBlox time message synchronization... (wait up to 10 seconds)\n"; + if (GnssTime_queue->timed_wait_and_pop(tow, 10000) == false) + { + std::cout << "uBlox time message synchronization error.\n"; + return; + } + + std::cout << "Waiting for PPS Samplestamp message synchronization... (wait up to 10 seconds)\n"; + if (Pps_queue->timed_wait_and_pop(pps, 10000) == false) + { + std::cout << "PPS IP message synchronization error.\n"; + return; + } + + //Get new PPS samplestamp and associate it to the corresponding uBlox TP message + while (receive_samples == true) + { + std::cout << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n"; + LOG(INFO) << "[" << pps.samplestamp << "][o:" << pps.overflow_reg << "] uBlox time message received with TOW=" << tow.tow_ms << "\n"; + //write timestamp information to timestamp metadata file: + //uint64_t: absolute sample counter from the beginning of sample capture associated to the rising edge of the PPS signal + // ppstimefile.write(reinterpret_cast(&pps.samplestamp), sizeof(uint64_t)); + //int32_t: Galileo/GPS Week Number associated to the rising edge of PPS signal + // ppstimefile.write(reinterpret_cast(&tow.week), sizeof(int32_t)); + //int32_t: Galileo/GPS TOW associated to the rising edge of PPS signal + // ppstimefile.write(reinterpret_cast(&tow.tow_ms), sizeof(int32_t)); + //record pps rise samplestamp associated to the absolute sample counter + //PPS rising edge must be associated with the corresponding uBlox time message (tx once a second) + + + if (GnssTime_queue->timed_wait_and_pop(tow, 2000) == false) + { + if (receive_samples == true) + { + std::cout << "ERROR: uBlox time message not received, check uBlox GNSS signal quality!\n"; + LOG(INFO) << "ERROR: uBlox time message not received!"; + } + break; + } + if (Pps_queue->timed_wait_and_pop(pps, 2000) == false) + { + if (receive_samples == true) + { + std::cout << "ERROR: PPS time message not received, check uBlox GNSS signal quality!\n"; + LOG(INFO) << "ERROR: PPS time message not received!"; + } + break; + } + if (pps.overflow_reg > 0) + { + if (receive_samples == true) + { + fpga_overflow = true; + std::cout << "ERROR: FPGA reported RX sample buffer overflow!\n"; + LOG(INFO) << "ERROR: FPGA reported RX sample buffer overflow!\n"; + } + break; + } + } +} +bool ad936x_iio_custom::start_sample_rx(bool ppsmode) +{ + //using queues of smart pointers to preallocated buffers + free_buffers.clear(); + used_buffers.clear(); + //preallocate buffers and use queues + std::cerr << "Allocating memory..\n"; + try + { + for (int n = 0; n < IIO_INPUTRAMFIFOSIZE; n++) + { + free_buffers.push(std::make_shared()); + } + } + catch (const std::exception &ex) + { + std::cout << "ERROR: Problem allocating RAM buffer: " << ex.what() << "\n"; + return false; + } + + //prepare capture channels + std::vector channels; + switch (n_channels) + { + case 1: + channels.push_back("voltage0"); //Channel 0 I + channels.push_back("voltage1"); //Channel 0 Q + break; + case 2: + channels.push_back("voltage0"); //Channel 0 I + channels.push_back("voltage1"); //Channel 0 Q + channels.push_back("voltage2"); //Channel 1 I + channels.push_back("voltage3"); //Channel 1 Q + break; + default: + channels.push_back("voltage0"); //Channel 0 I + channels.push_back("voltage1"); //Channel 0 Q + } + + receive_samples = true; + //start sample capture thread + capture_samples_thread = std::thread(&ad936x_iio_custom::capture, this, channels); + //start sample overflow detector + overflow_monitor_thread = std::thread(&ad936x_iio_custom::monitor_thread_fn, this); + + + //start PPS and GNSS Time capture thread + + if (ppsmode == true) + { + capture_time_thread = std::thread(&ad936x_iio_custom::get_PPS_timestamp, this); + } + return true; +} + +void ad936x_iio_custom::pop_sample_buffer(std::shared_ptr ¤t_buffer) +{ + used_buffers.wait_and_pop(current_buffer); +} + +void ad936x_iio_custom::push_sample_buffer(std::shared_ptr ¤t_buffer) +{ + free_buffers.push(current_buffer); +} + +void ad936x_iio_custom::capture(const std::vector &channels) +{ + if (check_device() == false) return; + + struct iio_buffer *rxbuf; + + std::vector channel_list; + + /* First disable all channels */ + unsigned int nb_channels; + nb_channels = iio_device_get_channels_count(stream_dev); + for (unsigned int i = 0; i < nb_channels; i++) + { + iio_channel_disable(iio_device_get_channel(stream_dev, i)); + } + + // enable channels + if (channels.empty()) + { + for (unsigned int i = 0; i < nb_channels; i++) + { + struct iio_channel *chn = + iio_device_get_channel(stream_dev, i); + + iio_channel_enable(chn); + channel_list.push_back(chn); + } + } + else + { + for (std::vector::const_iterator it = + channels.begin(); + it != channels.end(); ++it) + { + struct iio_channel *chn = + iio_device_find_channel(stream_dev, + it->c_str(), false); + if (!chn) + { + std::cerr << "Channel " << it->c_str() << " not found\n"; + return; + } + else + { + iio_channel_enable(chn); + channel_list.push_back(chn); + } + } + } + + const struct iio_data_format *format = iio_channel_get_data_format(channel_list[0]); + + std::cerr << "Format: length " << format->length + << " bits " << format->bits + << " shift " << format->shift + << " is_signed " << format->is_signed + << " is_fully_defined " << format->is_fully_defined + << " is_be " << format->is_be + << " with_scale " << format->with_scale + << " scale " << format->scale + << " repeat " << format->repeat << "\n"; + + rxbuf = iio_device_create_buffer(stream_dev, IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES, false); + if (!rxbuf) + { + std::cout << "Could not create RX buffer. \n"; + return; + } + + std::shared_ptr current_buffer; + ad936x_iio_samples *current_samples; + unsigned long items_in_buffer; + std::cerr << "Enter capture loop...\n"; + int ret; + int bytes_per_channel = static_cast(channels.size()) * 2; //each channel has two items in channels vector (I,Q). Each component has two bytes. + while (receive_samples == true) + { + free_buffers.wait_and_pop(current_buffer); + current_samples = current_buffer.get(); + ret = iio_buffer_refill(rxbuf); + if (ret < 0) + { + /* -EBADF happens when the buffer is cancelled */ + if (ret != -EBADF) + { + char err_buf[256]; + iio_strerror(-ret, err_buf, sizeof(err_buf)); + std::string error(err_buf); + + std::cerr << "Unable to refill buffer: " << error << std::endl; + iio_buffer_destroy(rxbuf); + return; + } + } + + // Demultiplex the samples of a given channel + int n_ch = 0; + for (auto it = std::begin(channel_list); it != std::end(channel_list); ++it) + { + current_samples->n_bytes[n_ch] = iio_channel_read_raw(*it, rxbuf, ¤t_samples->buffer[n_ch][0], IIO_MAX_BYTES_PER_CHANNEL); + current_samples->n_samples[n_ch] = current_samples->n_bytes[n_ch] / sizeof(short); + n_ch++; + } + + // old, valid only for one channel + //memcpy(¤t_samples->buffer[0], iio_buffer_start(rxbuf), ret); + + if (current_samples->n_bytes[0] == 0) return; + + used_buffers.push(current_buffer); + } + + iio_buffer_destroy(rxbuf); +} diff --git a/src/algorithms/signal_source/libs/ad936x_iio_custom.h b/src/algorithms/signal_source/libs/ad936x_iio_custom.h new file mode 100644 index 000000000..f8c1fe718 --- /dev/null +++ b/src/algorithms/signal_source/libs/ad936x_iio_custom.h @@ -0,0 +1,144 @@ +/*! + * \file ad936x_iio_custom.h + * \brief A direct IIO custom front-end driver for the AD936x AD front-end family with special FPGA custom functionalities. + * \author Javier Arribas, jarribas(at)cttc.es + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#ifndef SRC_LIBS_ad936x_iio_custom_H_ +#define SRC_LIBS_ad936x_iio_custom_H_ + +#include "concurrent_queue.h" +#include "gnss_time.h" +#include "pps_samplestamp.h" +#include +#include +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include "ad936x_iio_samples.h" +#include //multichip sync and high level functions +#include +#include + +class ad936x_iio_custom +{ +public: + ad936x_iio_custom(int debug_level_, int log_level_); + virtual ~ad936x_iio_custom(); + bool initialize_device(std::string pluto_device_uri, std::string board_type); + + bool init_config_ad9361_rx(long long bandwidth_, + long long sample_rate_, + long long freq_, + std::string rf_port_select_, + std::string rf_filter, + std::string gain_mode_rx0_, + std::string gain_mode_rx1_, + double rf_gain_rx0_, + double rf_gain_rx1_, + bool enable_ch0, + bool enable_ch1, + long long freq_2ch); + + bool calibrate(int ch, double bw_hz); + + double get_rx_gain(int ch_num); + bool setRXGain(int ch_num, std::string gain_mode, double gain_dB); + + bool set_antenna_port(int ch, int antenna_idx); + double get_frequency(int ch); + bool set_frequency(int ch, double freq_hz); + + bool start_sample_rx(bool ppsmode); + void stop_record(); + + void set_gnsstime_queue(std::shared_ptr> queue); + void set_pps_samplestamp_queue(std::shared_ptr> queue); + + bool get_rx_frequency(long long &freq_hz); + bool set_rx_frequency(long long freq_hz); + bool read_die_temp(double &temp_c); + + void pop_sample_buffer(std::shared_ptr ¤t_buffer); + + void push_sample_buffer(std::shared_ptr ¤t_buffer); + + int n_channels; + +private: + std::shared_ptr> GnssTime_queue; + std::shared_ptr> Pps_queue; + bool check_device(); + bool get_iio_param(iio_device *dev, const std::string ¶m, std::string &value); + void configure_params(struct iio_device *phy, + const std::vector ¶ms); + void set_params_rx(struct iio_device *phy_device, + unsigned long long frequency, + unsigned long samplerate, unsigned long bandwidth, + bool quadrature, bool rfdc, bool bbdc, + std::string gain1, double gain1_value, + std::string gain2, double gain2_value, + std::string port_select); + + bool config_ad9361_dds(uint64_t freq_rf_tx_hz_, + double tx_attenuation_db_, + int64_t freq_dds_tx_hz_, + double scale_dds_, + double phase_dds_deg_); + + void get_PPS_timestamp(); + void capture(const std::vector &channels); + + bool select_rf_filter(std::string rf_filter); + + void monitor_thread_fn(); + + void PlutoTxEnable(bool txon); + void setPlutoGpo(int p); + + //Device structure + struct iio_context *ctx; + struct iio_device *phy; + struct iio_device *stream_dev; + struct iio_device *dds_dev; + + //stream + + uint64_t sample_rate_sps; + + + int debug_level; + int log_level; + bool PPS_mode; + + std::mutex mtx; + std::condition_variable cv; + + boost::atomic receive_samples; + + boost::atomic fpga_overflow; + //using queues of smart pointers to preallocated buffers + Concurrent_Queue> free_buffers; + Concurrent_Queue> used_buffers; + + std::thread capture_samples_thread; + std::thread overflow_monitor_thread; + std::thread capture_time_thread; +}; + +#endif /* SRC_LIBS_ad936x_iio_custom_H_ */ diff --git a/src/algorithms/signal_source/libs/ad936x_iio_samples.cc b/src/algorithms/signal_source/libs/ad936x_iio_samples.cc new file mode 100644 index 000000000..7d1ef5681 --- /dev/null +++ b/src/algorithms/signal_source/libs/ad936x_iio_samples.cc @@ -0,0 +1,26 @@ +/*! + * \file ad936x_iio_samples.cc + * \brief A class that holds a custom sample buffer for Analog Devices AD936x family front-ends. + * \author Javier Arribas, jarribas(at)cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "ad936x_iio_samples.h" + +ad936x_iio_samples::ad936x_iio_samples() +{ + for (int n = 0; n < IIO_MAX_CH; n++) + { + n_bytes[n] = 0; + n_samples[n] = 0; + } +} diff --git a/src/algorithms/signal_source/libs/ad936x_iio_samples.h b/src/algorithms/signal_source/libs/ad936x_iio_samples.h new file mode 100644 index 000000000..5e932075c --- /dev/null +++ b/src/algorithms/signal_source/libs/ad936x_iio_samples.h @@ -0,0 +1,41 @@ +/*! + * \file ad936x_iio_samples.h + * \brief A class that holds a custom sample buffer for Analog Devices AD936x family front-ends. + * \author Javier Arribas, jarribas(at)cttc.es + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2022 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + + +#ifndef SRC_LIBS_ad936x_iio_samples_H_ +#define SRC_LIBS_ad936x_iio_samples_H_ + +#define IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES 32768 * 2 + +#define IIO_INPUTRAMFIFOSIZE 512 + +#define IIO_MAX_CH 4 +#define IIO_MAX_BYTES_PER_CHANNEL IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2 * 2 //(2-bytes per I + 2-bytes per Q) + +#include +#include +#include + +class ad936x_iio_samples +{ +public: + ad936x_iio_samples(); + uint32_t n_bytes[IIO_MAX_CH]; + uint32_t n_samples[IIO_MAX_CH]; + int16_t buffer[IIO_MAX_CH][IIO_DEFAULTAD936XAPIFIFOSIZE_SAMPLES * 2]; //16 bits I,Q samples buffers +}; + +#endif diff --git a/src/algorithms/signal_source/libs/pps_samplestamp.h b/src/algorithms/signal_source/libs/pps_samplestamp.h new file mode 100644 index 000000000..cdbc1276f --- /dev/null +++ b/src/algorithms/signal_source/libs/pps_samplestamp.h @@ -0,0 +1,20 @@ +/* ------------------------------------------------------------------------- + * + * Copyright (C) 2022 (see AUTHORS file for a list of contributors) + * + * + */ + +#ifndef IIOPPS_PPS_SAMPLESTAMP_H +#define IIOPPS_PPS_SAMPLESTAMP_H + +#include + +class PpsSamplestamp +{ +public: + uint64_t samplestamp; //PPS rising edge samples counter from the beginning of rx stream opperation. Notice that it is reseted to zero if sample buffer overflow is detected on the FPGA side + uint32_t overflow_reg; // >0 indicates overflow situation in the FPGA RX buffer +}; + +#endif diff --git a/src/algorithms/signal_source/libs/ppstcprx.cc b/src/algorithms/signal_source/libs/ppstcprx.cc new file mode 100644 index 000000000..0a10d8545 --- /dev/null +++ b/src/algorithms/signal_source/libs/ppstcprx.cc @@ -0,0 +1,154 @@ +/* + * ppstcprx.cc + * + * Created on: 28 feb 2022 + * Author: javier + */ + +#include "ppstcprx.h" +#include +#include +#include +#include + +pps_tcp_rx::pps_tcp_rx() +{ + // TODO Auto-generated constructor stub + is_connected = false; + clientSd = -1; +} + +pps_tcp_rx::~pps_tcp_rx() +{ + // TODO Auto-generated destructor stub +} + +void pps_tcp_rx::set_pps_samplestamp_queue(std::shared_ptr> queue) +{ + Pps_queue = std::move(queue); +} + +bool pps_tcp_rx::send_cmd(std::string cmd) +{ + if (is_connected == true) + { + // send call sends the data you specify as second param and it's length as 3rd param, also returns how many bytes were actually sent + auto bytes_sent = send(clientSd, cmd.data(), cmd.length(), 0); + if (bytes_sent <= 0) + { + std::cerr << "Connection terminated...\n"; + return false; + } + else + { + std::cout << "sent bytes..\n"; + } + } + else + { + return false; + } + return true; +} +void pps_tcp_rx::receive_pps(std::string ip_address, int port) +{ + //create a message buffer + char buf[1500]; + //setup a socket and connection tools + sockaddr_in sendSockAddr; + sendSockAddr.sin_family = AF_INET; + sendSockAddr.sin_addr.s_addr = + inet_addr(ip_address.c_str()); + sendSockAddr.sin_port = htons(port); + clientSd = socket(AF_INET, SOCK_STREAM, 0); + //try to connect... + int status = connect(clientSd, + (sockaddr *)&sendSockAddr, sizeof(sendSockAddr)); + if (status < 0) + { + std::cout << "pps_tcp_rx: Error connecting to PPS TCP server IP " << ip_address << " at port " << port << std::endl; + return; + } + std::string new_pps_line; + + is_connected = true; + while (true) + { + int numBytesRead = recv(clientSd, buf, sizeof(buf), 0); + if (numBytesRead > 0) + { + for (int i = 0; i < numBytesRead; i++) + { + char c = buf[i]; + if (c == '\n') + { + if (new_pps_line.length() > 0) + { + //std::cout << "pps_tcp_rx debug: " << new_pps_line << "\n"; + //parse string and push PPS data to the PPS queue + std::stringstream ss(new_pps_line); + std::vector data; + while (ss.good()) + { + std::string substr; + std::getline(ss, substr, ','); + data.push_back(substr); + } + if (data.size() >= 2) + { + PpsSamplestamp new_pps; + //sample counter + std::size_t found = data.at(0).find("sc="); + if (found != std::string::npos) + { + try + { + new_pps.samplestamp = std::strtoul(data.at(0).substr(found + 3).c_str(), NULL, 0); + } + catch (const std::exception &ex) + { + std::cout << "pps_tcp_rx debug: sc parse error str " << data.at(0) << "\n"; + } + } + else + { + std::cout << "pps_tcp_rx debug: sc parse error str " << data.at(0) << "\n"; + } + found = data.at(1).find("o="); + if (found != std::string::npos) + { + try + { + new_pps.overflow_reg = std::stoi(data.at(1).substr(found + 2).c_str(), NULL, 0); + } + catch (const std::exception &ex) + { + std::cout << "pps_tcp_rx debug: o parse error str " << data.at(0) << "\n"; + } + } + else + { + std::cout << "pps_tcp_rx debug: o parse error str " << data.at(1) << "\n"; + } + Pps_queue->push(new_pps); + //std::cout << "pps_tcp_rx debug: pps pushed!\n"; + } + else + { + std::cout << "pps_tcp_rx debug: protocol error!\n"; + } + new_pps_line = ""; + } + } + else + new_pps_line += c; + } + } + else + { + std::cout << "pps_tcp_rx: Socket disconnected!\n!"; + break; + } + } + is_connected = false; +} diff --git a/src/algorithms/signal_source/libs/ppstcprx.h b/src/algorithms/signal_source/libs/ppstcprx.h new file mode 100644 index 000000000..37c8de92f --- /dev/null +++ b/src/algorithms/signal_source/libs/ppstcprx.h @@ -0,0 +1,34 @@ +/* + * ppstcprx.h + * + * Created on: 28 feb 2022 + * Author: javier + */ + +#ifndef SRC_LIBS_PPSTCPRX_H_ +#define SRC_LIBS_PPSTCPRX_H_ +#include "concurrent_queue.h" +#include "pps_samplestamp.h" +#include +#include +#include +#include +#include +class pps_tcp_rx +{ +private: + std::shared_ptr> Pps_queue; + int clientSd; + +public: + volatile bool is_connected; + pps_tcp_rx(); + virtual ~pps_tcp_rx(); + + void receive_pps(std::string ip_address, int port); + bool send_cmd(std::string cmd); + + void set_pps_samplestamp_queue(std::shared_ptr> queue); +}; + +#endif /* SRC_LIBS_PPSTCPRX_H_ */ diff --git a/src/core/receiver/concurrent_queue.h b/src/core/receiver/concurrent_queue.h index fc5c97053..ac7be0f17 100644 --- a/src/core/receiver/concurrent_queue.h +++ b/src/core/receiver/concurrent_queue.h @@ -55,6 +55,18 @@ public: return the_queue.empty(); } + size_t size() const + { + std::unique_lock lock(the_mutex); + return the_queue.size(); + } + + void clear() + { + std::unique_lock lock(the_mutex); + the_queue = std::queue(); + } + bool try_pop(Data& popped_value) { std::unique_lock lock(the_mutex); diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index adba9c0df..d7bd03b97 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -153,6 +153,7 @@ #endif #if PLUTOSDR_DRIVER +#include "ad936x_custom_signal_source.h" #include "plutosdr_signal_source.h" #endif @@ -775,6 +776,12 @@ std::unique_ptr GNSSBlockFactory::GetBlock( out_streams, queue); block = std::move(block_); } + else if (implementation == "Ad936x_Custom_Signal_Source") + { + std::unique_ptr block_ = std::make_unique(configuration, role, in_streams, + out_streams, queue); + block = std::move(block_); + } #endif #if FMCOMMS2_DRIVER