From 4cccd97bb53b53317559957edaa0fe8364b8659b Mon Sep 17 00:00:00 2001 From: piyush0411 Date: Tue, 9 Jun 2020 01:02:52 +0530 Subject: [PATCH] PCPS based Acquisition Files for FPGA --- .../galileo_e5b_pcps_acquisition_fpga.cc | 290 ++++++++++++++++++ .../galileo_e5b_pcps_acquisition_fpga.h | 213 +++++++++++++ 2 files changed, 503 insertions(+) create mode 100644 src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.cc create mode 100644 src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.h diff --git a/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.cc b/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.cc new file mode 100644 index 000000000..6e1f18017 --- /dev/null +++ b/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.cc @@ -0,0 +1,290 @@ +/*! + * \file galileo_e5b_pcps_acquisition_fpga.cc + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * Galileo E5b data and pilot Signals for the FPGA + * \author Piyush Gupta, 2020. piyush04111999@gmail.com + * \note Code added as part of GSoC 2020 Program. + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2020 (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. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ------------------------------------------------------------------------- + */ + +#include "galileo_e5b_pcps_acquisition_fpga.h" +#include "Galileo_E5b.h" +#include "configuration_interface.h" +#include "galileo_e5_signal_processing.h" +#include "gnss_sdr_flags.h" +#include +#include // for fft_complex +#include // for gr_complex +#include // for volk_32fc_conjugate_32fc +#include +#include // for copy_n +#include // for abs, pow, floor +#include // for complex + +GalileoE5bPcpsAcquisitionFpga::GalileoE5bPcpsAcquisitionFpga(ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams) : role_(role), + in_streams_(in_streams), + out_streams_(out_streams) +{ + pcpsconf_fpga_t acq_parameters; + configuration_ = configuration; + std::string default_dump_filename = "../data/acquisition.dat"; + + DLOG(INFO) << "Role " << role; + + int64_t fs_in_deprecated = configuration_->property("GNSS-SDR.internal_fs_hz", 32000000); + int64_t fs_in = configuration_->property("GNSS-SDR.internal_fs_sps", fs_in_deprecated); + + acq_parameters.repeat_satellite = configuration_->property(role + ".repeat_satellite", false); + DLOG(INFO) << role << " satellite repeat = " << acq_parameters.repeat_satellite; + + uint32_t downsampling_factor = configuration_->property(role + ".downsampling_factor", 1); + acq_parameters.downsampling_factor = downsampling_factor; + fs_in = fs_in / downsampling_factor; + + acq_parameters.fs_in = fs_in; + + doppler_max_ = configuration_->property(role + ".doppler_max", 5000); + if (FLAGS_doppler_max != 0) + { + doppler_max_ = FLAGS_doppler_max; + } + acq_parameters.doppler_max = doppler_max_; + + acq_pilot_ = configuration_->property(role + ".acquire_pilot", false); + acq_iq_ = configuration_->property(role + ".acquire_iq", false); + if (acq_iq_) + { + acq_pilot_ = false; + } + + auto code_length = static_cast(std::round(static_cast(fs_in) / GALILEO_E5B_CODE_CHIP_RATE_CPS * static_cast(GALILEO_E5B_CODE_LENGTH_CHIPS))); + acq_parameters.code_length = code_length; + + // The FPGA can only use FFT lengths that are a power of two. + float nbits = ceilf(log2f(static_cast(code_length) * 2.0)); + uint32_t nsamples_total = pow(2, nbits); + uint32_t select_queue_Fpga = configuration_->property(role + ".select_queue_Fpga", 1); + acq_parameters.select_queue_Fpga = select_queue_Fpga; + std::string default_device_name = "/dev/uio0"; + std::string device_name = configuration_->property(role + ".devicename", default_device_name); + acq_parameters.device_name = device_name; + acq_parameters.samples_per_code = nsamples_total; + + acq_parameters.excludelimit = static_cast(1 + ceil((1.0 / GALILEO_E5B_CODE_CHIP_RATE_CPS) * static_cast(fs_in))); + + // compute all the GALILEO E5b PRN Codes (this is done only once in the class constructor in order to avoid re-computing the PRN codes every time + // a channel is assigned) + auto fft_if = std::unique_ptr(new gr::fft::fft_complex(nsamples_total, true)); // Direct FFT + volk_gnsssdr::vector> code(nsamples_total); // Buffer for local code + volk_gnsssdr::vector> fft_codes_padded(nsamples_total); + d_all_fft_codes_ = std::vector(nsamples_total * GALILEO_E5B_NUMBER_OF_CODES); // memory containing all the possible fft codes for PRN 0 to 32 + + float max; // temporary maxima search + int32_t tmp; + int32_t tmp2; + int32_t local_code; + int32_t fft_data; + + for (uint32_t PRN = 1; PRN <= GALILEO_E5B_NUMBER_OF_CODES; PRN++) + { + std::array signal_; + signal_[0] = '7'; + signal_[2] = '\0'; + + if (acq_iq_) + { + signal_[1] = 'X'; + } + else if (acq_pilot_) + { + signal_[1] = 'Q'; + } + else + { + signal_[1] = 'I'; + } + + galileo_e5_b_code_gen_complex_sampled(code, PRN, signal_, fs_in, 0); + + for (uint32_t s = code_length; s < 2 * code_length; s++) + { + code[s] = code[s - code_length]; + } + + // fill in zero padding + for (uint32_t s = 2 * code_length; s < nsamples_total; s++) + { + code[s] = std::complex(0.0, 0.0); + } + + std::copy_n(code.data(), nsamples_total, fft_if->get_inbuf()); // copy to FFT buffer + fft_if->execute(); // Run the FFT of local code + volk_32fc_conjugate_32fc(fft_codes_padded.data(), fft_if->get_outbuf(), nsamples_total); // conjugate values + + max = 0; // initialize maximum value + for (uint32_t i = 0; i < nsamples_total; i++) // search for maxima + { + if (std::abs(fft_codes_padded[i].real()) > max) + { + max = std::abs(fft_codes_padded[i].real()); + } + if (std::abs(fft_codes_padded[i].imag()) > max) + { + max = std::abs(fft_codes_padded[i].imag()); + } + } + // map the FFT to the dynamic range of the fixed point values an copy to buffer containing all FFTs + // and package codes in a format that is ready to be written to the FPGA + for (uint32_t i = 0; i < nsamples_total; i++) + { + tmp = static_cast(floor(fft_codes_padded[i].real() * (pow(2, quant_bits_local_code - 1) - 1) / max)); + tmp2 = static_cast(floor(fft_codes_padded[i].imag() * (pow(2, quant_bits_local_code - 1) - 1) / max)); + local_code = (tmp & select_lsbits) | ((tmp2 * shl_code_bits) & select_msbits); // put together the real part and the imaginary part + fft_data = local_code & select_all_code_bits; + d_all_fft_codes_[i + (nsamples_total * (PRN - 1))] = fft_data; + } + } + + acq_parameters.all_fft_codes = d_all_fft_codes_.data(); + + // reference for the FPGA FFT-IFFT attenuation factor + acq_parameters.total_block_exp = configuration_->property(role + ".total_block_exp", 13); + + acq_parameters.num_doppler_bins_step2 = configuration_->property(role + ".second_nbins", 4); + acq_parameters.doppler_step2 = configuration_->property(role + ".second_doppler_step", 125.0); + acq_parameters.make_2_steps = configuration_->property(role + ".make_two_steps", false); + acq_parameters.max_num_acqs = configuration_->property(role + ".max_num_acqs", 2); + acquisition_fpga_ = pcps_make_acquisition_fpga(acq_parameters); + + channel_ = 0; + doppler_step_ = 0; + gnss_synchro_ = nullptr; + + if (in_streams_ > 1) + { + LOG(ERROR) << "This implementation only supports one input stream"; + } + if (out_streams_ > 0) + { + LOG(ERROR) << "This implementation does not provide an output stream"; + } +} + + +void GalileoE5bPcpsAcquisitionFpga::stop_acquisition() +{ + // this command causes the SW to reset the HW. + acquisition_fpga_->reset_acquisition(); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_threshold(float threshold) +{ + DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold; + acquisition_fpga_->set_threshold(threshold); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_doppler_max(unsigned int doppler_max) +{ + doppler_max_ = doppler_max; + acquisition_fpga_->set_doppler_max(doppler_max_); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_doppler_step(unsigned int doppler_step) +{ + doppler_step_ = doppler_step; + acquisition_fpga_->set_doppler_step(doppler_step_); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_doppler_center(int doppler_center) +{ + doppler_center_ = doppler_center; + acquisition_fpga_->set_doppler_center(doppler_center_); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_gnss_synchro(Gnss_Synchro* gnss_synchro) +{ + gnss_synchro_ = gnss_synchro; + acquisition_fpga_->set_gnss_synchro(gnss_synchro_); +} + + +signed int GalileoE5bPcpsAcquisitionFpga::mag() +{ + return acquisition_fpga_->mag(); +} + + +void GalileoE5bPcpsAcquisitionFpga::init() +{ + acquisition_fpga_->init(); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_local_code() +{ + acquisition_fpga_->set_local_code(); +} + + +void GalileoE5bPcpsAcquisitionFpga::reset() +{ + acquisition_fpga_->set_active(true); +} + + +void GalileoE5bPcpsAcquisitionFpga::set_state(int state) +{ + acquisition_fpga_->set_state(state); +} + + +void GalileoE5bPcpsAcquisitionFpga::connect(gr::top_block_sptr top_block) +{ + if (top_block) + { + /* top_block is not null */ + }; + // Nothing to connect +} + + +void GalileoE5bPcpsAcquisitionFpga::disconnect(gr::top_block_sptr top_block) +{ + if (top_block) + { + /* top_block is not null */ + }; + // Nothing to disconnect +} + + +gr::basic_block_sptr GalileoE5bPcpsAcquisitionFpga::get_left_block() +{ + return nullptr; +} + + +gr::basic_block_sptr GalileoE5bPcpsAcquisitionFpga::get_right_block() +{ + return nullptr; +} diff --git a/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.h b/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.h new file mode 100644 index 000000000..2c7cd7aad --- /dev/null +++ b/src/algorithms/acquisition/adapters/galileo_e5b_pcps_acquisition_fpga.h @@ -0,0 +1,213 @@ +/*! + * \file galileo_e5b_pcps_acquisition_fpga.h + * \brief Adapts a PCPS acquisition block to an AcquisitionInterface for + * Galileo E5b data and pilot Signals for the FPGA + * \author Piyush Gupta, 2020. piyush04111999@gmail.com + * \note Code added as part of GSoC 2020 Program. + * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2020 (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. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_GALILEO_E5B_PCPS_ACQUISITION_FPGA_H +#define GNSS_SDR_GALILEO_E5B_PCPS_ACQUISITION_FPGA_H + + +#include "channel_fsm.h" +#include "gnss_synchro.h" +#include "pcps_acquisition_fpga.h" +#include +#include +#include + +class ConfigurationInterface; + +/*! + * \brief This class adapts a PCPS acquisition block off-loaded on an FPGA + * to an AcquisitionInterface for Galileo E5b signals + */ +class GalileoE5bPcpsAcquisitionFpga : public AcquisitionInterface +{ +public: + /*! + * \brief Constructor + */ + GalileoE5bPcpsAcquisitionFpga(ConfigurationInterface* configuration, + const std::string& role, + unsigned int in_streams, + unsigned int out_streams); + + /*! + * \brief Destructor + */ + ~GalileoE5bPcpsAcquisitionFpga() = default; + + /*! + * \brief Role + */ + inline std::string role() override + { + return role_; + } + + /*! + * \brief Returns "Galileo_E5b_Pcps_Acquisition_Fpga" + */ + inline std::string implementation() override + { + return "Galileo_E5b_PCPS_Acquisition_FPGA"; + } + + /*! + * \brief Returns size of lv_16sc_t + */ + inline size_t item_size() override + { + return sizeof(int16_t); + } + + /*! + * \brief Connect + */ + void connect(gr::top_block_sptr top_block) override; + + /*! + * \brief Disconnect + */ + void disconnect(gr::top_block_sptr top_block) override; + + /*! + * \brief Get left block + */ + gr::basic_block_sptr get_left_block() override; + + /*! + * \brief Get right block + */ + gr::basic_block_sptr get_right_block() override; + + /*! + * \brief Set acquisition/tracking common Gnss_Synchro object pointer + * to efficiently exchange synchronization data between acquisition and + * tracking blocks + */ + void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) override; + + /*! + * \brief Set acquisition channel unique ID + */ + inline void set_channel(unsigned int channel) override + { + channel_ = channel; + acquisition_fpga_->set_channel(channel_); + } + + /*! + * \brief Set channel fsm associated to this acquisition instance + */ + inline void set_channel_fsm(std::weak_ptr channel_fsm) override + { + channel_fsm_ = channel_fsm; + acquisition_fpga_->set_channel_fsm(channel_fsm); + } + + /*! + * \brief Set statistics threshold of PCPS algorithm + */ + void set_threshold(float threshold) override; + + /*! + * \brief Set maximum Doppler off grid search + */ + void set_doppler_max(unsigned int doppler_max) override; + + /*! + * \brief Set Doppler steps for the grid search + */ + void set_doppler_step(unsigned int doppler_step) override; + + /*! + * \brief Set Doppler center for the grid search + */ + void set_doppler_center(int doppler_center) override; + + /*! + * \brief Initializes acquisition algorithm. + */ + void init() override; + + /*! + * \brief Sets local Galileo E5b code for PCPS acquisition algorithm. + */ + void set_local_code() override; + + /*! + * \brief Returns the maximum peak of grid search + */ + signed int mag() override; + + /*! + * \brief Restart acquisition algorithm + */ + void reset() override; + + /*! + * \brief If set to 1, ensures that acquisition starts at the + * first available sample. + * \param state - int=1 forces start of acquisition + */ + void set_state(int state) override; + + /*! + * \brief This function is only used in the unit tests + */ + void set_single_doppler_flag(unsigned int single_doppler_flag); + + /*! + * \brief Stop running acquisition + */ + void stop_acquisition() override; + + /*! + * \brief Set resampler latency + */ + void set_resampler_latency(uint32_t latency_samples __attribute__((unused))) override{}; + +private: + // the following flags are FPGA-specific and they are using arrange the values of the fft of the local code in the way the FPGA + // expects. This arrangement is done in the initialisation to avoid consuming unnecessary clock cycles during tracking. + static const uint32_t quant_bits_local_code = 16; + static const uint32_t select_lsbits = 0x0000FFFF; // Select the 10 LSbits out of a 20-bit word + static const uint32_t select_msbits = 0xFFFF0000; // Select the 10 MSbits out of a 20-bit word + static const uint32_t select_all_code_bits = 0xFFFFFFFF; // Select a 20 bit word + static const uint32_t shl_code_bits = 65536; // shift left by 10 bits + + ConfigurationInterface* configuration_; + pcps_acquisition_fpga_sptr acquisition_fpga_; + std::string item_type_; + std::string dump_filename_; + std::string role_; + bool acq_pilot_; + bool acq_iq_; + uint32_t channel_; + std::weak_ptr channel_fsm_; + uint32_t doppler_max_; + uint32_t doppler_step_; + int32_t doppler_center_; + unsigned int in_streams_; + unsigned int out_streams_; + Gnss_Synchro* gnss_synchro_; + std::vector d_all_fft_codes_; // memory that contains all the code ffts +}; + +#endif // GNSS_SDR_GALILEO_E5B_PCPS_ACQUISITION_FPGA_H