From ea35f33c8398667215a5d2506bb667c6a96bcc26 Mon Sep 17 00:00:00 2001 From: Javier Arribas Date: Wed, 20 Jan 2016 18:24:03 +0100 Subject: [PATCH] Added native input sample interface support for 16 bits integer complex in PCPS_Acquisition (added the _sc variant). Now the PCPS acquisiton adapter requires no conversion when the receiver works with 16 bits integer complex samples. --- .../acquisition/adapters/CMakeLists.txt | 1 + .../adapters/gps_l1_ca_pcps_acquisition.cc | 184 ++++--- .../adapters/gps_l1_ca_pcps_acquisition.h | 3 + .../gnuradio_blocks/CMakeLists.txt | 4 +- .../gnuradio_blocks/pcps_acquisition_sc.cc | 472 ++++++++++++++++++ .../gnuradio_blocks/pcps_acquisition_sc.h | 244 +++++++++ 6 files changed, 836 insertions(+), 72 deletions(-) create mode 100644 src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.cc create mode 100644 src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.h diff --git a/src/algorithms/acquisition/adapters/CMakeLists.txt b/src/algorithms/acquisition/adapters/CMakeLists.txt index e72f05fe3..5d272975a 100644 --- a/src/algorithms/acquisition/adapters/CMakeLists.txt +++ b/src/algorithms/acquisition/adapters/CMakeLists.txt @@ -50,6 +50,7 @@ include_directories( ${GFlags_INCLUDE_DIRS} ${GNURADIO_RUNTIME_INCLUDE_DIRS} ${GNURADIO_BLOCKS_INCLUDE_DIRS} + ${VOLK_GNSSSDR_INCLUDE_DIRS} ) file(GLOB ACQ_ADAPTER_HEADERS "*.h") diff --git a/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.cc b/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.cc index 196450fe2..3cda8287b 100644 --- a/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.cc +++ b/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.cc @@ -84,36 +84,36 @@ GpsL1CaPcpsAcquisition::GpsL1CaPcpsAcquisition( code_ = new gr_complex[vector_length_]; - // if (item_type_.compare("gr_complex") == 0 ) - // { - item_size_ = sizeof(gr_complex); - acquisition_cc_ = pcps_make_acquisition_cc(sampled_ms_, max_dwells_, + if (item_type_.compare("cshort") == 0 ) + { + item_size_ = sizeof(lv_16sc_t); + acquisition_sc_ = pcps_make_acquisition_sc(sampled_ms_, max_dwells_, + shift_resolution_, if_, fs_in_, code_length_, code_length_, + bit_transition_flag_, queue_, dump_, dump_filename_); + DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; + + }else{ + item_size_ = sizeof(gr_complex); + acquisition_cc_ = pcps_make_acquisition_cc(sampled_ms_, max_dwells_, shift_resolution_, if_, fs_in_, code_length_, code_length_, bit_transition_flag_, queue_, dump_, dump_filename_); + DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; + } stream_to_vector_ = gr::blocks::stream_to_vector::make(item_size_, vector_length_); - DLOG(INFO) << "stream_to_vector(" << stream_to_vector_->unique_id() << ")"; - DLOG(INFO) << "acquisition(" << acquisition_cc_->unique_id() << ")"; - // } - - if (item_type_.compare("cshort") == 0) - { - cshort_to_float_x2_ = make_cshort_to_float_x2(); - float_to_complex_ = gr::blocks::float_to_complex::make(); - } + //now is supported natively by the acquisition (_sc variant) +// if (item_type_.compare("cshort") == 0) +// { +// cshort_to_float_x2_ = make_cshort_to_float_x2(); +// float_to_complex_ = gr::blocks::float_to_complex::make(); +// } if (item_type_.compare("cbyte") == 0) { cbyte_to_float_x2_ = make_complex_byte_to_float_x2(); float_to_complex_ = gr::blocks::float_to_complex::make(); } - //} - //else - // { - // LOG(WARNING) << item_type_ - // << " unknown acquisition item type"; - // } channel_ = 0; threshold_ = 0.0; doppler_max_ = 0; @@ -132,10 +132,13 @@ GpsL1CaPcpsAcquisition::~GpsL1CaPcpsAcquisition() void GpsL1CaPcpsAcquisition::set_channel(unsigned int channel) { channel_ = channel; - //if (item_type_.compare("gr_complex") == 0) - //{ - acquisition_cc_->set_channel(channel_); - //} + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_channel(channel_); + }else{ + acquisition_cc_->set_channel(channel_); + } + } @@ -155,30 +158,39 @@ void GpsL1CaPcpsAcquisition::set_threshold(float threshold) DLOG(INFO) << "Channel " << channel_ << " Threshold = " << threshold_; - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_threshold(threshold_); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_threshold(threshold_); + }else{ + acquisition_cc_->set_threshold(threshold_); + } } void GpsL1CaPcpsAcquisition::set_doppler_max(unsigned int doppler_max) { doppler_max_ = doppler_max; - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_doppler_max(doppler_max_); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_doppler_max(doppler_max_); + }else{ + acquisition_cc_->set_doppler_max(doppler_max_); + } } void GpsL1CaPcpsAcquisition::set_doppler_step(unsigned int doppler_step) { doppler_step_ = doppler_step; - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_doppler_step(doppler_step_); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_doppler_step(doppler_step_); + }else{ + acquisition_cc_->set_doppler_step(doppler_step_); + } } @@ -187,39 +199,49 @@ void GpsL1CaPcpsAcquisition::set_channel_queue( concurrent_queue *channel_internal_queue) { channel_internal_queue_ = channel_internal_queue; - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_channel_queue(channel_internal_queue_); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_channel_queue(channel_internal_queue_); + }else{ + acquisition_cc_->set_channel_queue(channel_internal_queue_); + } } void GpsL1CaPcpsAcquisition::set_gnss_synchro(Gnss_Synchro* gnss_synchro) { gnss_synchro_ = gnss_synchro; - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_gnss_synchro(gnss_synchro_); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_gnss_synchro(gnss_synchro_); + }else{ + acquisition_cc_->set_gnss_synchro(gnss_synchro_); + } } signed int GpsL1CaPcpsAcquisition::mag() { - // // if (item_type_.compare("gr_complex") == 0) - // { - return acquisition_cc_->mag(); - // } - // else - // { - // return 0; - // } + if (item_type_.compare("cshort") == 0) + { + return acquisition_sc_->mag(); + }else{ + return acquisition_cc_->mag(); + } } void GpsL1CaPcpsAcquisition::init() { - acquisition_cc_->init(); + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->init(); + }else{ + acquisition_cc_->init(); + } + set_local_code(); } @@ -238,7 +260,13 @@ void GpsL1CaPcpsAcquisition::set_local_code() sizeof(gr_complex)*code_length_); } - acquisition_cc_->set_local_code(code_); + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_local_code(code_); + }else{ + acquisition_cc_->set_local_code(code_); + } delete[] code; // } @@ -247,18 +275,23 @@ void GpsL1CaPcpsAcquisition::set_local_code() void GpsL1CaPcpsAcquisition::reset() { - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_active(true); - // } + + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_active(true); + }else{ + acquisition_cc_->set_active(true); + } } void GpsL1CaPcpsAcquisition::set_state(int state) { - // if (item_type_.compare("gr_complex") == 0) - // { - acquisition_cc_->set_state(state); - // } + if (item_type_.compare("cshort") == 0) + { + acquisition_sc_->set_state(state); + }else{ + acquisition_cc_->set_state(state); + } } @@ -291,10 +324,12 @@ void GpsL1CaPcpsAcquisition::connect(gr::top_block_sptr top_block) } else if (item_type_.compare("cshort") == 0) { - top_block->connect(cshort_to_float_x2_, 0, float_to_complex_, 0); - top_block->connect(cshort_to_float_x2_, 1, float_to_complex_, 1); - top_block->connect(float_to_complex_, 0, stream_to_vector_, 0); - top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); + //top_block->connect(cshort_to_float_x2_, 0, float_to_complex_, 0); + //top_block->connect(cshort_to_float_x2_, 1, float_to_complex_, 1); + //top_block->connect(float_to_complex_, 0, stream_to_vector_, 0); + //top_block->connect(stream_to_vector_, 0, acquisition_cc_, 0); + top_block->connect(stream_to_vector_, 0, acquisition_sc_, 0); + } else if (item_type_.compare("cbyte") == 0) { @@ -320,10 +355,11 @@ void GpsL1CaPcpsAcquisition::disconnect(gr::top_block_sptr top_block) { // Since a short-based acq implementation is not available, // we just convert cshorts to gr_complex - top_block->disconnect(cshort_to_float_x2_, 0, float_to_complex_, 0); - top_block->disconnect(cshort_to_float_x2_, 1, float_to_complex_, 1); - top_block->disconnect(float_to_complex_, 0, stream_to_vector_, 0); - top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); + //top_block->disconnect(cshort_to_float_x2_, 0, float_to_complex_, 0); + //top_block->disconnect(cshort_to_float_x2_, 1, float_to_complex_, 1); + //top_block->disconnect(float_to_complex_, 0, stream_to_vector_, 0); + //top_block->disconnect(stream_to_vector_, 0, acquisition_cc_, 0); + top_block->disconnect(stream_to_vector_, 0, acquisition_sc_, 0); } else if (item_type_.compare("cbyte") == 0) { @@ -349,7 +385,8 @@ gr::basic_block_sptr GpsL1CaPcpsAcquisition::get_left_block() } else if (item_type_.compare("cshort") == 0) { - return cshort_to_float_x2_; + //return cshort_to_float_x2_; + return stream_to_vector_; } else if (item_type_.compare("cbyte") == 0) { @@ -365,6 +402,11 @@ gr::basic_block_sptr GpsL1CaPcpsAcquisition::get_left_block() gr::basic_block_sptr GpsL1CaPcpsAcquisition::get_right_block() { - return acquisition_cc_; + if (item_type_.compare("cshort") == 0) + { + return acquisition_sc_; + }else{ + return acquisition_cc_; + } } diff --git a/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h b/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h index 52665947d..3fd8db743 100644 --- a/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h +++ b/src/algorithms/acquisition/adapters/gps_l1_ca_pcps_acquisition.h @@ -43,8 +43,10 @@ #include "gnss_synchro.h" #include "acquisition_interface.h" #include "pcps_acquisition_cc.h" +#include "pcps_acquisition_sc.h" #include "cshort_to_float_x2.h" #include "complex_byte_to_float_x2.h" +#include @@ -145,6 +147,7 @@ public: private: ConfigurationInterface* configuration_; pcps_acquisition_cc_sptr acquisition_cc_; + pcps_acquisition_sc_sptr acquisition_sc_; gr::blocks::stream_to_vector::sptr stream_to_vector_; gr::blocks::float_to_complex::sptr float_to_complex_; cshort_to_float_x2_sptr cshort_to_float_x2_; diff --git a/src/algorithms/acquisition/gnuradio_blocks/CMakeLists.txt b/src/algorithms/acquisition/gnuradio_blocks/CMakeLists.txt index 5a1d0d5ce..b75be3c64 100644 --- a/src/algorithms/acquisition/gnuradio_blocks/CMakeLists.txt +++ b/src/algorithms/acquisition/gnuradio_blocks/CMakeLists.txt @@ -19,6 +19,7 @@ set(ACQ_GR_BLOCKS_SOURCES pcps_acquisition_cc.cc + pcps_acquisition_sc.cc pcps_multithread_acquisition_cc.cc pcps_assisted_acquisition_cc.cc pcps_acquisition_fine_doppler_cc.cc @@ -42,6 +43,7 @@ include_directories( ${GLOG_INCLUDE_DIRS} ${GFlags_INCLUDE_DIRS} ${GNURADIO_RUNTIME_INCLUDE_DIRS} + ${VOLK_GNSSSDR_INCLUDE_DIRS} ) @@ -57,5 +59,5 @@ endif(OPENCL_FOUND) file(GLOB ACQ_GR_BLOCKS_HEADERS "*.h") add_library(acq_gr_blocks ${ACQ_GR_BLOCKS_SOURCES} ${ACQ_GR_BLOCKS_HEADERS}) source_group(Headers FILES ${ACQ_GR_BLOCKS_HEADERS}) -target_link_libraries(acq_gr_blocks gnss_sp_libs gnss_system_parameters ${GNURADIO_RUNTIME_LIBRARIES} ${GNURADIO_FFT_LIBRARIES} ${VOLK_LIBRARIES} ${OPT_LIBRARIES}) +target_link_libraries(acq_gr_blocks gnss_sp_libs gnss_system_parameters ${GNURADIO_RUNTIME_LIBRARIES} ${GNURADIO_FFT_LIBRARIES} ${VOLK_LIBRARIES} ${VOLK_GNSSSDR_LIBRARIES} ${OPT_LIBRARIES}) diff --git a/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.cc b/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.cc new file mode 100644 index 000000000..2648ad5b5 --- /dev/null +++ b/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.cc @@ -0,0 +1,472 @@ +/*! + * \file pcps_acquisition_sc.cc + * \brief This class implements a Parallel Code Phase Search Acquisition + * \authors
    + *
  • Javier Arribas, 2011. jarribas(at)cttc.es + *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com + *
  • Marc Molina, 2013. marc.molina.pena@gmail.com + *
+ * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#include "pcps_acquisition_sc.h" +#include +#include +#include +#include +#include +#include "gnss_signal_processing.h" +#include "control_message_factory.h" +#include + +using google::LogMessage; + +pcps_acquisition_sc_sptr pcps_make_acquisition_sc( + unsigned int sampled_ms, unsigned int max_dwells, + unsigned int doppler_max, long freq, long fs_in, + int samples_per_ms, int samples_per_code, + bool bit_transition_flag, + gr::msg_queue::sptr queue, bool dump, + std::string dump_filename) +{ + + return pcps_acquisition_sc_sptr( + new pcps_acquisition_sc(sampled_ms, max_dwells, doppler_max, freq, fs_in, samples_per_ms, + samples_per_code, bit_transition_flag, queue, dump, dump_filename)); +} + +pcps_acquisition_sc::pcps_acquisition_sc( + unsigned int sampled_ms, unsigned int max_dwells, + unsigned int doppler_max, long freq, long fs_in, + int samples_per_ms, int samples_per_code, + bool bit_transition_flag, + gr::msg_queue::sptr queue, bool dump, + std::string dump_filename) : + gr::block("pcps_acquisition_sc", + gr::io_signature::make(1, 1, sizeof(lv_16sc_t) * sampled_ms * samples_per_ms * ( bit_transition_flag ? 2 : 1 )), + gr::io_signature::make(0, 0, 0)) +{ + d_sample_counter = 0; // SAMPLE COUNTER + d_active = false; + d_state = 0; + d_queue = queue; + d_freq = freq; + d_fs_in = fs_in; + d_samples_per_ms = samples_per_ms; + d_samples_per_code = samples_per_code; + d_sampled_ms = sampled_ms; + d_max_dwells = max_dwells; + d_well_count = 0; + d_doppler_max = doppler_max; + d_fft_size = d_sampled_ms * d_samples_per_ms; + d_mag = 0; + d_input_power = 0.0; + d_num_doppler_bins = 0; + d_bit_transition_flag = bit_transition_flag; + d_threshold = 0.0; + d_doppler_step = 250; + d_code_phase = 0; + d_test_statistics = 0.0; + d_channel = 0; + d_doppler_freq = 0.0; + + //set_relative_rate( 1.0/d_fft_size ); + + // COD: + // Experimenting with the overlap/save technique for handling bit trannsitions + // The problem: Circular correlation is asynchronous with the received code. + // In effect the first code phase used in the correlation is the current + // estimate of the code phase at the start of the input buffer. If this is 1/2 + // of the code period a bit transition would move all the signal energy into + // adjacent frequency bands at +/- 1/T where T is the integration time. + // + // We can avoid this by doing linear correlation, effectively doubling the + // size of the input buffer and padding the code with zeros. + if( d_bit_transition_flag ) + { + d_fft_size *= 2; + d_max_dwells = 1; + } + + d_fft_codes = static_cast(volk_malloc(d_fft_size * sizeof(gr_complex), volk_get_alignment())); + d_magnitude = static_cast(volk_malloc(d_fft_size * sizeof(float), volk_get_alignment())); + //temporary storage for the input conversion from 16sc to float 32fc + d_in_32fc = static_cast(volk_malloc(d_fft_size * sizeof(gr_complex), volk_get_alignment())); + + // Direct FFT + d_fft_if = new gr::fft::fft_complex(d_fft_size, true); + + // Inverse FFT + d_ifft = new gr::fft::fft_complex(d_fft_size, false); + + // For dumping samples into a file + d_dump = dump; + d_dump_filename = dump_filename; + + d_gnss_synchro = 0; + d_channel_internal_queue = 0; + d_grid_doppler_wipeoffs = 0; +} + +pcps_acquisition_sc::~pcps_acquisition_sc() +{ + if (d_num_doppler_bins > 0) + { + for (unsigned int i = 0; i < d_num_doppler_bins; i++) + { + volk_free(d_grid_doppler_wipeoffs[i]); + } + delete[] d_grid_doppler_wipeoffs; + } + + volk_free(d_fft_codes); + volk_free(d_magnitude); + volk_free(d_in_32fc); + + delete d_ifft; + delete d_fft_if; + + if (d_dump) + { + d_dump_file.close(); + } +} + +void pcps_acquisition_sc::set_local_code(std::complex * code) +{ + // COD + // Here we want to create a buffer that looks like this: + // [ 0 0 0 ... 0 c_0 c_1 ... c_L] + // where c_i is the local code and there are L zeros and L chips + int offset = 0; + if( d_bit_transition_flag ) + { + std::fill_n( d_fft_if->get_inbuf(), d_samples_per_code, gr_complex( 0.0, 0.0 ) ); + offset = d_samples_per_code; + } + memcpy(d_fft_if->get_inbuf() + offset, code, sizeof(gr_complex) * d_samples_per_code); + d_fft_if->execute(); // We need the FFT of local code + volk_32fc_conjugate_32fc(d_fft_codes, d_fft_if->get_outbuf(), d_fft_size); +} + +void pcps_acquisition_sc::init() +{ + d_gnss_synchro->Acq_delay_samples = 0.0; + d_gnss_synchro->Acq_doppler_hz = 0.0; + d_gnss_synchro->Acq_samplestamp_samples = 0; + d_mag = 0.0; + d_input_power = 0.0; + + d_num_doppler_bins = ceil( static_cast(static_cast(d_doppler_max) - static_cast(-d_doppler_max)) / static_cast(d_doppler_step)); + + // Create the carrier Doppler wipeoff signals + d_grid_doppler_wipeoffs = new gr_complex*[d_num_doppler_bins]; + + for (unsigned int doppler_index = 0; doppler_index < d_num_doppler_bins; doppler_index++) + { + d_grid_doppler_wipeoffs[doppler_index] = static_cast(volk_malloc(d_fft_size * sizeof(gr_complex), volk_get_alignment())); + int doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; + complex_exp_gen(d_grid_doppler_wipeoffs[doppler_index], -d_freq - doppler, d_fs_in, d_fft_size); + } +} + + + +void pcps_acquisition_sc::set_state(int state) + { + d_state = state; + if (d_state == 1) + { + d_gnss_synchro->Acq_delay_samples = 0.0; + d_gnss_synchro->Acq_doppler_hz = 0.0; + d_gnss_synchro->Acq_samplestamp_samples = 0; + d_well_count = 0; + d_mag = 0.0; + d_input_power = 0.0; + d_test_statistics = 0.0; + } + else if (d_state == 0) + {} + else + { + LOG(ERROR) << "State can only be set to 0 or 1"; + } + } + +int pcps_acquisition_sc::general_work(int noutput_items, + gr_vector_int &ninput_items, gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + /* + * By J.Arribas, L.Esteve and M.Molina + * Acquisition strategy (Kay Borre book + CFAR threshold): + * 1. Compute the input signal power estimation + * 2. Doppler serial search loop + * 3. Perform the FFT-based circular convolution (parallel time search) + * 4. Record the maximum peak and the associated synchronization parameters + * 5. Compute the test statistics and compare to the threshold + * 6. Declare positive or negative acquisition using a message queue + */ + + int acquisition_message = -1; //0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL + + switch (d_state) + { + case 0: + { + if (d_active) + { + //restart acquisition variables + d_gnss_synchro->Acq_delay_samples = 0.0; + d_gnss_synchro->Acq_doppler_hz = 0.0; + d_gnss_synchro->Acq_samplestamp_samples = 0; + d_well_count = 0; + d_mag = 0.0; + d_input_power = 0.0; + d_test_statistics = 0.0; + + d_state = 1; + } + + d_sample_counter += d_fft_size * ninput_items[0]; // sample counter + consume_each(ninput_items[0]); + + //DLOG(INFO) << "Consumed " << ninput_items[0] << " items"; + + break; + } + + case 1: + { + // initialize acquisition algorithm + int doppler; + unsigned int indext = 0; + float magt = 0.0; + const lv_16sc_t *in = (const lv_16sc_t *)input_items[0]; //Get the input samples pointer + int effective_fft_size = ( d_bit_transition_flag ? d_fft_size/2 : d_fft_size ); + + //TODO: optimize the signal processing chain to not use gr_complex. This is a temporary solution + volk_gnsssdr_16ic_convert_32fc(d_in_32fc,in,effective_fft_size); + + float fft_normalization_factor = static_cast(d_fft_size) * static_cast(d_fft_size); + + d_input_power = 0.0; + d_mag = 0.0; + + d_sample_counter += d_fft_size; // sample counter + + d_well_count++; + + DLOG(INFO) << "Channel: " << d_channel + << " , doing acquisition of satellite: " << d_gnss_synchro->System << " "<< d_gnss_synchro->PRN + << " ,sample stamp: " << d_sample_counter << ", threshold: " + << d_threshold << ", doppler_max: " << d_doppler_max + << ", doppler_step: " << d_doppler_step; + + // 1- Compute the input signal power estimation + volk_32fc_magnitude_squared_32f(d_magnitude, d_in_32fc, d_fft_size); + volk_32f_accumulator_s32f(&d_input_power, d_magnitude, d_fft_size); + d_input_power /= static_cast(d_fft_size); + // 2- Doppler frequency search loop + for (unsigned int doppler_index=0; doppler_index < d_num_doppler_bins; doppler_index++) + { + // doppler search steps + + doppler = -static_cast(d_doppler_max) + d_doppler_step * doppler_index; + + volk_32fc_x2_multiply_32fc(d_fft_if->get_inbuf(), d_in_32fc, + d_grid_doppler_wipeoffs[doppler_index], d_fft_size); + + // 3- Perform the FFT-based convolution (parallel time search) + // Compute the FFT of the carrier wiped--off incoming signal + d_fft_if->execute(); + + // Multiply carrier wiped--off, Fourier transformed incoming signal + // with the local FFT'd code reference using SIMD operations with VOLK library + volk_32fc_x2_multiply_32fc(d_ifft->get_inbuf(), + d_fft_if->get_outbuf(), d_fft_codes, d_fft_size); + + // compute the inverse FFT + d_ifft->execute(); + + // Search maximum + size_t offset = ( d_bit_transition_flag ? effective_fft_size : 0 ); + volk_32fc_magnitude_squared_32f(d_magnitude, d_ifft->get_outbuf() + offset, effective_fft_size); + volk_32f_index_max_16u(&indext, d_magnitude, effective_fft_size); + + // Normalize the maximum value to correct the scale factor introduced by FFTW + magt = d_magnitude[indext] / (fft_normalization_factor * fft_normalization_factor); + + // 4- record the maximum peak and the associated synchronization parameters + if (d_mag < magt) + { + d_mag = magt; + + // In case that d_bit_transition_flag = true, we compare the potentially + // new maximum test statistics (d_mag/d_input_power) with the value in + // d_test_statistics. When the second dwell is being processed, the value + // of d_mag/d_input_power could be lower than d_test_statistics (i.e, + // the maximum test statistics in the previous dwell is greater than + // current d_mag/d_input_power). Note that d_test_statistics is not + // restarted between consecutive dwells in multidwell operation. + if (d_test_statistics < (d_mag / d_input_power) || !d_bit_transition_flag) + { + d_gnss_synchro->Acq_delay_samples = static_cast(indext % d_samples_per_code); + d_gnss_synchro->Acq_doppler_hz = static_cast(doppler); + d_gnss_synchro->Acq_samplestamp_samples = d_sample_counter; + + // 5- Compute the test statistics and compare to the threshold + //d_test_statistics = 2 * d_fft_size * d_mag / d_input_power; + d_test_statistics = d_mag / d_input_power; + } + } + + // Record results to file if required + if (d_dump) + { + std::stringstream filename; + std::streamsize n = 2 * sizeof(float) * (d_fft_size); // complex file write + filename.str(""); + + boost::filesystem::path p = d_dump_filename; + filename << p.parent_path().string() + << boost::filesystem::path::preferred_separator + << p.stem().string() + << "_" << d_gnss_synchro->System + <<"_" << d_gnss_synchro->Signal << "_sat_" + << d_gnss_synchro->PRN << "_doppler_" + << doppler + << p.extension().string(); + + DLOG(INFO) << "Writing ACQ out to " << filename.str(); + + d_dump_file.open(filename.str().c_str(), std::ios::out | std::ios::binary); + d_dump_file.write((char*)d_ifft->get_outbuf(), n); //write directly |abs(x)|^2 in this Doppler bin? + d_dump_file.close(); + } + } + + if (!d_bit_transition_flag) + { + if (d_test_statistics > d_threshold) + { + d_state = 2; // Positive acquisition + } + else if (d_well_count == d_max_dwells) + { + d_state = 3; // Negative acquisition + } + } + else + { + if (d_well_count == d_max_dwells) // d_max_dwells = 2 + { + if (d_test_statistics > d_threshold) + { + d_state = 2; // Positive acquisition + } + else + { + d_state = 3; // Negative acquisition + } + } + } + + consume_each(1); + + DLOG(INFO) << "Done. Consumed 1 item."; + + break; + } + + case 2: + { + // 6.1- Declare positive acquisition using a message queue + DLOG(INFO) << "positive acquisition"; + DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; + DLOG(INFO) << "sample_stamp " << d_sample_counter; + DLOG(INFO) << "test statistics value " << d_test_statistics; + DLOG(INFO) << "test statistics threshold " << d_threshold; + DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; + DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; + DLOG(INFO) << "magnitude " << d_mag; + DLOG(INFO) << "input signal power " << d_input_power; + + d_active = false; + d_state = 0; + + d_sample_counter += d_fft_size * ninput_items[0]; // sample counter + consume_each(ninput_items[0]); + + acquisition_message = 1; + d_channel_internal_queue->push(acquisition_message); + + break; + } + + case 3: + { + // 6.2- Declare negative acquisition using a message queue + DLOG(INFO) << "negative acquisition"; + DLOG(INFO) << "satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN; + DLOG(INFO) << "sample_stamp " << d_sample_counter; + DLOG(INFO) << "test statistics value " << d_test_statistics; + DLOG(INFO) << "test statistics threshold " << d_threshold; + DLOG(INFO) << "code phase " << d_gnss_synchro->Acq_delay_samples; + DLOG(INFO) << "doppler " << d_gnss_synchro->Acq_doppler_hz; + DLOG(INFO) << "magnitude " << d_mag; + DLOG(INFO) << "input signal power " << d_input_power; + + d_active = false; + d_state = 0; + + d_sample_counter += d_fft_size * ninput_items[0]; // sample counter + consume_each(ninput_items[0]); + acquisition_message = 2; + d_channel_internal_queue->push(acquisition_message); + + break; + } + } + + output_items.clear(); // removes a warning + return noutput_items; +} + + +//void pcps_acquisition_sc::forecast (int noutput_items, gr_vector_int &ninput_items_required) +//{ + //// COD: + //// For zero-padded case we need one extra code period + //if( d_bit_transition_flag ) + //{ + //ninput_items_required[0] = noutput_items*(d_samples_per_code * d_max_dwells + d_samples_per_code); + //} + //else + //{ + //ninput_items_required[0] = noutput_items*d_fft_size*d_max_dwells; + //} +//} diff --git a/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.h b/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.h new file mode 100644 index 000000000..7270d7827 --- /dev/null +++ b/src/algorithms/acquisition/gnuradio_blocks/pcps_acquisition_sc.h @@ -0,0 +1,244 @@ +/*! + * \file pcps_acquisition_sc.h + * \brief This class implements a Parallel Code Phase Search Acquisition + * + * Acquisition strategy (Kay Borre book + CFAR threshold). + *
    + *
  1. Compute the input signal power estimation + *
  2. Doppler serial search loop + *
  3. Perform the FFT-based circular convolution (parallel time search) + *
  4. Record the maximum peak and the associated synchronization parameters + *
  5. Compute the test statistics and compare to the threshold + *
  6. Declare positive or negative acquisition using a message queue + *
+ * + * Kay Borre book: K.Borre, D.M.Akos, N.Bertelsen, P.Rinder, and S.H.Jensen, + * "A Software-Defined GPS and Galileo Receiver. A Single-Frequency + * Approach", Birkha user, 2007. pp 81-84 + * + * \authors
    + *
  • Javier Arribas, 2011. jarribas(at)cttc.es + *
  • Luis Esteve, 2012. luis(at)epsilon-formacion.com + *
  • Marc Molina, 2013. marc.molina.pena@gmail.com + *
+ * + * ------------------------------------------------------------------------- + * + * Copyright (C) 2010-2015 (see AUTHORS file for a list of contributors) + * + * GNSS-SDR is a software defined Global Navigation + * Satellite Systems receiver + * + * This file is part of GNSS-SDR. + * + * GNSS-SDR is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GNSS-SDR is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNSS-SDR. If not, see . + * + * ------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_PCPS_ACQUISITION_SC_H_ +#define GNSS_SDR_PCPS_ACQUISITION_SC_H_ + +#include +#include +#include +#include +#include +#include +#include "concurrent_queue.h" +#include "gnss_synchro.h" + +class pcps_acquisition_sc; + +typedef boost::shared_ptr pcps_acquisition_sc_sptr; + +pcps_acquisition_sc_sptr +pcps_make_acquisition_sc(unsigned int sampled_ms, unsigned int max_dwells, + unsigned int doppler_max, long freq, long fs_in, + int samples_per_ms, int samples_per_code, + bool bit_transition_flag, + gr::msg_queue::sptr queue, bool dump, + std::string dump_filename); + +/*! + * \brief This class implements a Parallel Code Phase Search Acquisition. + * + * Check \ref Navitec2012 "An Open Source Galileo E1 Software Receiver", + * Algorithm 1, for a pseudocode description of this implementation. + */ +class pcps_acquisition_sc: public gr::block +{ +private: + friend pcps_acquisition_sc_sptr + pcps_make_acquisition_sc(unsigned int sampled_ms, unsigned int max_dwells, + unsigned int doppler_max, long freq, long fs_in, + int samples_per_ms, int samples_per_code, + bool bit_transition_flag, + gr::msg_queue::sptr queue, bool dump, + std::string dump_filename); + + pcps_acquisition_sc(unsigned int sampled_ms, unsigned int max_dwells, + unsigned int doppler_max, long freq, long fs_in, + int samples_per_ms, int samples_per_code, + bool bit_transition_flag, + gr::msg_queue::sptr queue, bool dump, + std::string dump_filename); + + void calculate_magnitudes(gr_complex* fft_begin, int doppler_shift, + int doppler_offset); + + long d_fs_in; + long d_freq; + int d_samples_per_ms; + int d_samples_per_code; + //unsigned int d_doppler_resolution; + float d_threshold; + std::string d_satellite_str; + unsigned int d_doppler_max; + unsigned int d_doppler_step; + unsigned int d_sampled_ms; + unsigned int d_max_dwells; + unsigned int d_well_count; + unsigned int d_fft_size; + unsigned long int d_sample_counter; + gr_complex** d_grid_doppler_wipeoffs; + unsigned int d_num_doppler_bins; + gr_complex* d_fft_codes; + gr_complex* d_in_32fc; + gr::fft::fft_complex* d_fft_if; + gr::fft::fft_complex* d_ifft; + Gnss_Synchro *d_gnss_synchro; + unsigned int d_code_phase; + float d_doppler_freq; + float d_mag; + float* d_magnitude; + float d_input_power; + float d_test_statistics; + bool d_bit_transition_flag; + gr::msg_queue::sptr d_queue; + concurrent_queue *d_channel_internal_queue; + std::ofstream d_dump_file; + bool d_active; + int d_state; + bool d_dump; + unsigned int d_channel; + std::string d_dump_filename; + +public: + /*! + * \brief Default destructor. + */ + ~pcps_acquisition_sc(); + + /*! + * \brief Set acquisition/tracking common Gnss_Synchro object pointer + * to exchange synchronization data between acquisition and tracking blocks. + * \param p_gnss_synchro Satellite information shared by the processing blocks. + */ + void set_gnss_synchro(Gnss_Synchro* p_gnss_synchro) + { + d_gnss_synchro = p_gnss_synchro; + } + + /*! + * \brief Returns the maximum peak of grid search. + */ + unsigned int mag() + { + return d_mag; + } + + /*! + * \brief Initializes acquisition algorithm. + */ + void init(); + + /*! + * \brief Sets local code for PCPS acquisition algorithm. + * \param code - Pointer to the PRN code. + */ + void set_local_code(std::complex * code); + + /*! + * \brief Starts acquisition algorithm, turning from standby mode to + * active mode + * \param active - bool that activates/deactivates the block. + */ + void set_active(bool active) + { + d_active = active; + } + + /*! + * \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); + + /*! + * \brief Set acquisition channel unique ID + * \param channel - receiver channel. + */ + void set_channel(unsigned int channel) + { + d_channel = channel; + } + + /*! + * \brief Set statistics threshold of PCPS algorithm. + * \param threshold - Threshold for signal detection (check \ref Navitec2012, + * Algorithm 1, for a definition of this threshold). + */ + void set_threshold(float threshold) + { + d_threshold = threshold; + } + + /*! + * \brief Set maximum Doppler grid search + * \param doppler_max - Maximum Doppler shift considered in the grid search [Hz]. + */ + void set_doppler_max(unsigned int doppler_max) + { + d_doppler_max = doppler_max; + } + + /*! + * \brief Set Doppler steps for the grid search + * \param doppler_step - Frequency bin of the search grid [Hz]. + */ + void set_doppler_step(unsigned int doppler_step) + { + d_doppler_step = doppler_step; + } + + /*! + * \brief Set tracking channel internal queue. + * \param channel_internal_queue - Channel's internal blocks information queue. + */ + void set_channel_queue(concurrent_queue *channel_internal_queue) + { + d_channel_internal_queue = channel_internal_queue; + } + + /*! + * \brief Parallel Code Phase Search Acquisition signal processing. + */ + int general_work(int noutput_items, gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +#endif /* GNSS_SDR_PCPS_ACQUISITION_SC_H_*/