1
0
mirror of https://github.com/gnss-sdr/gnss-sdr synced 2024-12-13 19:50:34 +00:00

updated fpga-related unit tests + fixed fpga acquisition log of sample stamp + stop tracking message unlocks channel (the fpga multicorrelator stops blocking the flow of samples)

This commit is contained in:
Marc Majoral 2019-12-16 18:44:22 +01:00
parent 6993890587
commit eb0df94336
6 changed files with 1861 additions and 1221 deletions

View File

@ -134,7 +134,7 @@ void pcps_acquisition_fpga::send_positive_acquisition()
// 0=STOP_CHANNEL 1=ACQ_SUCCEES 2=ACQ_FAIL
DLOG(INFO) << "positive acquisition"
<< ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN
<< ", sample_stamp " << d_sample_counter
<< ", sample_stamp " << d_gnss_synchro->Acq_samplestamp_samples
<< ", test statistics value " << d_test_statistics
<< ", test statistics threshold " << d_threshold
<< ", code phase " << d_gnss_synchro->Acq_delay_samples
@ -153,7 +153,7 @@ void pcps_acquisition_fpga::send_negative_acquisition()
// Declare negative acquisition using a message port
DLOG(INFO) << "negative acquisition"
<< ", satellite " << d_gnss_synchro->System << " " << d_gnss_synchro->PRN
<< ", sample_stamp " << d_sample_counter
<< ", sample_stamp " << d_gnss_synchro->Acq_samplestamp_samples
<< ", test statistics value " << d_test_statistics
<< ", test statistics threshold " << d_threshold
<< ", code phase " << d_gnss_synchro->Acq_delay_samples

View File

@ -61,10 +61,10 @@ public:
// FSM EVENTS
bool Event_start_acquisition();
bool Event_start_acquisition_fpga();
bool Event_valid_acquisition();
virtual bool Event_valid_acquisition();
bool Event_stop_channel();
bool Event_failed_acquisition_repeat();
bool Event_failed_acquisition_no_repeat();
virtual bool Event_failed_acquisition_repeat();
virtual bool Event_failed_acquisition_no_repeat();
bool Event_failed_tracking_standby();
private:

View File

@ -1467,6 +1467,8 @@ void dll_pll_veml_tracking_fpga::stop_tracking()
{
// interrupt the tracking loops
d_stop_tracking = true;
// let the samples pass through
multicorrelator_fpga->unlock_channel();
}

View File

@ -29,6 +29,11 @@
* -------------------------------------------------------------------------
*/
#include "fpga_switch.h"
#include <pthread.h> // for pthread stuff
#include "concurrent_queue.h"
#include "gnss_block_factory.h"
#include "gnss_block_interface.h"
@ -55,205 +60,32 @@
#include <gnuradio/analog/sig_source_c.h>
#endif
#define DMA_ACQ_TRANSFER_SIZE 2046 // DMA transfer size for the acquisition
#define RX_SIGNAL_MAX_VALUE 127 // 2^7 - 1 for 8-bit signed values
#define NTIMES_CYCLE_THROUGH_RX_SAMPLES_FILE 50 // number of times we cycle through the file containing the received samples
#define ONE_SECOND 1000000 // one second in microseconds
#define FLOAT_SIZE (sizeof(float)) // size of the float variable in characters
// thread that reads the file containing the received samples, scales the samples to the dynamic range of the fixed point values, sends
// the samples to the DMA and finally it stops the top block
void thread_acquisition_send_rx_samples(gr::top_block_sptr top_block,
const char *file_name)
struct DMA_handler_args_acq_test
{
FILE *rx_signal_file; // file descriptor
int file_length; // length of the file containing the received samples
int dma_descr; // DMA descriptor
// sleep for 1 second to give some time to GNSS-SDR to activate the acquisition module.
// the acquisition module does not block the RX buffer before activation.
// If this process starts sending samples straight ahead without waiting it could occur that
// the first samples are lost. This is normal behaviour in a real receiver but this is not what
// we want for the test
usleep(ONE_SECOND);
char *buffer_float; // temporary buffer to convert from binary char to float and from float to char
signed char *buffer_DMA; // temporary buffer to store the samples to be sent to the DMA
buffer_float = reinterpret_cast<char *>(malloc(FLOAT_SIZE)); // allocate space for the temporary buffer
if (!buffer_float)
{
std::cerr << "Memory error!" << std::endl;
return;
}
rx_signal_file = fopen(file_name, "rb"); // file containing the received signal
if (!rx_signal_file)
{
std::cerr << "Unable to open file!" << std::endl;
free(buffer_float);
return;
}
// determine the length of the file that contains the received signal
fseek(rx_signal_file, 0, SEEK_END);
file_length = ftell(rx_signal_file);
fseek(rx_signal_file, 0, SEEK_SET);
// first step: check for the maximum value of the received signal
float max = 0;
float *pointer_float;
pointer_float = reinterpret_cast<float *>(&buffer_float[0]);
for (int k = 0; k < file_length; k = k + FLOAT_SIZE)
{
size_t result = fread(buffer_float, FLOAT_SIZE, 1, rx_signal_file);
if (result != FLOAT_SIZE)
{
std::cerr << "Error reading buffer" << std::endl;
}
if (fabs(pointer_float[0]) > max)
{
max = (pointer_float[0]);
}
}
// go back to the beginning of the file containing the received samples
fseek(rx_signal_file, 0, SEEK_SET);
// allocate memory for the samples to be transferred to the DMA
buffer_DMA = (signed char *)malloc(DMA_ACQ_TRANSFER_SIZE);
if (!buffer_DMA)
{
std::cerr << "Memory error!" << std::endl;
free(buffer_float);
fclose(rx_signal_file);
return;
}
// open the DMA descriptor
dma_descr = open("/dev/loop_tx", O_WRONLY);
if (dma_descr < 0)
{
std::cerr << "Can't open loop device\n";
free(buffer_float);
free(buffer_DMA);
fclose(rx_signal_file);
return;
}
// cycle through the file containing the received samples
for (int k = 0; k < NTIMES_CYCLE_THROUGH_RX_SAMPLES_FILE; k++)
{
fseek(rx_signal_file, 0, SEEK_SET);
int transfer_size;
int num_transferred_samples = 0;
while (num_transferred_samples < static_cast<int>((file_length / FLOAT_SIZE)))
{
if (((file_length / FLOAT_SIZE) - num_transferred_samples) > DMA_ACQ_TRANSFER_SIZE)
{
transfer_size = DMA_ACQ_TRANSFER_SIZE;
num_transferred_samples = num_transferred_samples + DMA_ACQ_TRANSFER_SIZE;
}
else
{
transfer_size = file_length / FLOAT_SIZE - num_transferred_samples;
num_transferred_samples = file_length / FLOAT_SIZE;
}
for (int t = 0; t < transfer_size; t++)
{
size_t result = fread(buffer_float, FLOAT_SIZE, 1, rx_signal_file);
if (result != FLOAT_SIZE)
{
std::cerr << "Error reading buffer" << std::endl;
}
// specify (float) (int) for a quantization maximizing the dynamic range
buffer_DMA[t] = static_cast<signed char>((pointer_float[0] * (RX_SIGNAL_MAX_VALUE - 1) / max));
}
// send_acquisition_gps_input_samples(buffer_DMA, transfer_size, dma_descr);
assert(transfer_size == write(dma_descr, &buffer_DMA[0], transfer_size));
}
}
fclose(rx_signal_file);
free(buffer_float);
free(buffer_DMA);
close(dma_descr);
// when all the samples are sent stop the top block
top_block->stop();
}
// ######## GNURADIO BLOCK MESSAGE RECEVER #########
class GpsL1CaPcpsAcquisitionTestFpga_msg_rx;
using GpsL1CaPcpsAcquisitionTest_msg_fpga_rx_sptr = boost::shared_ptr<GpsL1CaPcpsAcquisitionTestFpga_msg_rx>;
GpsL1CaPcpsAcquisitionTest_msg_fpga_rx_sptr GpsL1CaPcpsAcquisitionTestFpga_msg_rx_make();
class GpsL1CaPcpsAcquisitionTestFpga_msg_rx : public gr::block
{
private:
friend GpsL1CaPcpsAcquisitionTest_msg_fpga_rx_sptr GpsL1CaPcpsAcquisitionTestFpga_msg_rx_make();
void msg_handler_events(pmt::pmt_t msg);
GpsL1CaPcpsAcquisitionTestFpga_msg_rx();
public:
int rx_message;
~GpsL1CaPcpsAcquisitionTestFpga_msg_rx(); //!< Default destructor
std::string file;
int32_t nsamples_tx;
int32_t skip_used_samples;
unsigned int freq_band; // 0 for GPS L1/ Galileo E1, 1 for GPS L5/Galileo E5
};
GpsL1CaPcpsAcquisitionTest_msg_fpga_rx_sptr GpsL1CaPcpsAcquisitionTestFpga_msg_rx_make()
struct acquisition_handler_args_acq_test
{
return GpsL1CaPcpsAcquisitionTest_msg_fpga_rx_sptr(
new GpsL1CaPcpsAcquisitionTestFpga_msg_rx());
}
void GpsL1CaPcpsAcquisitionTestFpga_msg_rx::msg_handler_events(pmt::pmt_t msg)
{
try
{
int64_t message = pmt::to_long(std::move(msg));
rx_message = message;
}
catch (boost::bad_any_cast &e)
{
LOG(WARNING) << "msg_handler_telemetry Bad any cast!";
rx_message = 0;
}
}
GpsL1CaPcpsAcquisitionTestFpga_msg_rx::GpsL1CaPcpsAcquisitionTestFpga_msg_rx() : gr::block("GpsL1CaPcpsAcquisitionTestFpga_msg_rx",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(0, 0, 0))
{
this->message_port_register_in(pmt::mp("events"));
this->set_msg_handler(pmt::mp("events"),
boost::bind(&GpsL1CaPcpsAcquisitionTestFpga_msg_rx::msg_handler_events, this, _1));
rx_message = 0;
}
GpsL1CaPcpsAcquisitionTestFpga_msg_rx::~GpsL1CaPcpsAcquisitionTestFpga_msg_rx() = default;
std::shared_ptr<AcquisitionInterface> acquisition;
};
class GpsL1CaPcpsAcquisitionTestFpga : public ::testing::Test
{
protected:
GpsL1CaPcpsAcquisitionTestFpga()
{
factory = std::make_shared<GNSSBlockFactory>();
config = std::make_shared<InMemoryConfiguration>();
item_size = sizeof(gr_complex);
gnss_synchro = Gnss_Synchro();
}
public:
bool acquire_signal();
std::string implementation = "GPS_L1_CA_DLL_PLL_Tracking_Fpga";
std::vector<Gnss_Synchro> gnss_synchro_vec;
static const int32_t TEST_ACQ_SKIP_SAMPLES = 1024;
static const int BASEBAND_SAMPLING_FREQ = 4000000;
protected:
GpsL1CaPcpsAcquisitionTestFpga();
~GpsL1CaPcpsAcquisitionTestFpga() = default;
void init();
@ -263,9 +95,389 @@ protected:
std::shared_ptr<InMemoryConfiguration> config;
Gnss_Synchro gnss_synchro;
size_t item_size;
unsigned int doppler_max;
unsigned int doppler_step;
unsigned int nsamples_to_transfer;
};
GpsL1CaPcpsAcquisitionTestFpga::GpsL1CaPcpsAcquisitionTestFpga()
{
factory = std::make_shared<GNSSBlockFactory>();
config = std::make_shared<InMemoryConfiguration>();
item_size = sizeof(gr_complex);
gnss_synchro = Gnss_Synchro();
doppler_max = 5000;
doppler_step = 100;
}
void* handler_DMA_acq_test(void* arguments)
{
const int MAX_INPUT_SAMPLES_TOTAL = 16384;
auto* args = (struct DMA_handler_args_acq_test*)arguments;
std::string Filename = args->file; // input filename
int32_t skip_used_samples = args->skip_used_samples;
int32_t nsamples_tx = args->nsamples_tx;
std::vector<int8_t> input_samples(MAX_INPUT_SAMPLES_TOTAL * 2);
std::vector<int8_t> input_samples2(MAX_INPUT_SAMPLES_TOTAL * 2);
std::vector<int8_t> input_samples_dma(MAX_INPUT_SAMPLES_TOTAL * 2 * 2);
bool file_completed = false;
int32_t nsamples_remaining;
int32_t nsamples_block_size;
unsigned int dma_index;
int tx_fd; // DMA descriptor
std::ifstream infile;
infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
try
{
infile.open(Filename, std::ios::binary);
}
catch (const std::ifstream::failure &e)
{
std::cerr << "Exception opening file " << Filename << std::endl;
return nullptr;
}
//**************************************************************************
// Open DMA device
//**************************************************************************
tx_fd = open("/dev/loop_tx", O_WRONLY);
if (tx_fd < 0)
{
std::cout << "Cannot open loop device" << std::endl;
return nullptr;
}
//**************************************************************************
// Open input file
//**************************************************************************
uint32_t skip_samples = 0; //static_cast<uint32_t>(FLAGS_skip_samples);
if (skip_samples + skip_used_samples > 0)
{
try
{
infile.ignore((skip_samples + skip_used_samples) * 2);
}
catch (const std::ifstream::failure &e)
{
std::cerr << "Exception reading file " << Filename << std::endl;
}
}
nsamples_remaining = nsamples_tx;
nsamples_block_size = 0;
while (file_completed == false)
{
dma_index = 0;
if (nsamples_remaining > MAX_INPUT_SAMPLES_TOTAL)
{
nsamples_block_size = MAX_INPUT_SAMPLES_TOTAL;
}
else
{
nsamples_block_size = nsamples_remaining;
}
try
{
// 2 bytes per complex sample
infile.read(reinterpret_cast<char *>(input_samples.data()), nsamples_block_size * 2);
}
catch (const std::ifstream::failure &e)
{
std::cerr << "Exception reading file " << Filename << std::endl;
}
for (int index0 = 0; index0 < (nsamples_block_size * 2); index0 += 2)
{
if (args->freq_band == 0)
{
// channel 1 (queue 1) -> E5/L5
input_samples_dma[dma_index] = 0;
input_samples_dma[dma_index + 1] = 0;
// channel 0 (queue 0) -> E1/L1
input_samples_dma[dma_index + 2] = input_samples[index0];
input_samples_dma[dma_index + 3] = input_samples[index0 + 1];
}
else
{
// channel 1 (queue 1) -> E5/L5
input_samples_dma[dma_index] = input_samples[index0]/16;
input_samples_dma[dma_index + 1] = input_samples[index0 + 1];
// channel 0 (queue 0) -> E1/L1
input_samples_dma[dma_index + 2] = 0;
input_samples_dma[dma_index + 3] = 0;
}
dma_index += 4;
}
if (write(tx_fd, input_samples_dma.data(), nsamples_block_size * 2 * 2) != nsamples_block_size * 2 * 2)
{
std::cerr << "Error: DMA could not send all the required samples " << std::endl;
}
// Throttle the DMA
std::this_thread::sleep_for(std::chrono::milliseconds(1));
nsamples_remaining -= nsamples_block_size;
if (nsamples_remaining == 0)
{
file_completed = true;
}
}
try
{
infile.close();
}
catch (const std::ifstream::failure &e)
{
std::cerr << "Exception closing files " << Filename << std::endl;
}
try
{
close(tx_fd);
}
catch (const std::ifstream::failure &e)
{
std::cerr << "Exception closing loop device " << std::endl;
}
return nullptr;
}
void* handler_acquisition_acq_test(void* arguments)
{
// the acquisition is a blocking function so we have to
// create a thread
auto* args = (struct acquisition_handler_args_acq_test*)arguments;
args->acquisition->reset();
return nullptr;
}
// When using the FPGA the acquisition class calls the states
// of the channel finite state machine directly. This is done
// in order to reduce the latency of the receiver when going
// from acquisition to tracking. In order to execute the
// acquisition in the unit tests we need to create a derived
// class of the channel finite state machine. Some of the states
// of the channel state machine are modified here, in order to
// simplify the instantiation of the acquisition class in the
// unit test.
class ChannelFsm_acq_test: public ChannelFsm
{
public:
bool Event_valid_acquisition() override
{
acquisition_successful = true;
return true;
}
bool Event_failed_acquisition_repeat() override
{
acquisition_successful = false;
return true;
}
bool Event_failed_acquisition_no_repeat() override
{
acquisition_successful = false;
return true;
}
bool Event_check_test_result()
{
return acquisition_successful;
}
void Event_clear_test_result()
{
acquisition_successful = false;
}
private:
bool acquisition_successful;
};
bool GpsL1CaPcpsAcquisitionTestFpga::acquire_signal()
{
pthread_t thread_DMA, thread_acquisition;
// 1. Setup GNU Radio flowgraph (file_source -> Acquisition_10m)
int SV_ID = 1; // initial sv id
// fsm
std::shared_ptr<ChannelFsm_acq_test> channel_fsm_;
channel_fsm_ = std::make_shared<ChannelFsm_acq_test>();
bool acquisition_successful;
// Satellite signal definition
Gnss_Synchro tmp_gnss_synchro;
tmp_gnss_synchro.Channel_ID = 0;
std::shared_ptr<AcquisitionInterface> acquisition;
std::string System_and_Signal;
std::string signal;
struct DMA_handler_args_acq_test args;
struct acquisition_handler_args_acq_test args_acq;
std::string file = "data/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
args.file = file; // DMA file configuration
// instantiate the FPGA switch and set the
// switch position to DMA.
std::shared_ptr<Fpga_Switch> switch_fpga;
switch_fpga = std::make_shared<Fpga_Switch>("/dev/uio1");
switch_fpga->set_switch_position(0); // set switch position to DMA
// create the correspondign acquisition block according to the desired tracking signal
tmp_gnss_synchro.System = 'G';
signal = "1C";
const char* str = signal.c_str(); // get a C style null terminated string
std::memcpy(static_cast<void*>(tmp_gnss_synchro.Signal), str, 3); // copy string into synchro char array: 2 char + null
tmp_gnss_synchro.PRN = SV_ID;
System_and_Signal = "GPS L1 CA";
const std::string& role = "Acquisition";
acquisition = std::make_shared<GpsL1CaPcpsAcquisitionFpga>(config.get(), "Acquisition", 0, 0);
args.freq_band = 1; // frequency band on which the DMA has to transfer the samples
acquisition->set_gnss_synchro(&tmp_gnss_synchro);
acquisition->set_channel_fsm(channel_fsm_);
acquisition->set_channel(1);
acquisition->set_doppler_max(doppler_max);
acquisition->set_doppler_step(doppler_step);
acquisition->set_doppler_center(0);
acquisition->set_threshold(0.001);
std::chrono::time_point<std::chrono::system_clock> start, end;
std::chrono::duration<double> elapsed_seconds;
start = std::chrono::system_clock::now();
bool start_msg = true;
nsamples_to_transfer = static_cast<unsigned int>(std::round(static_cast<double>(BASEBAND_SAMPLING_FREQ) / (GPS_L1_CA_CODE_RATE_CPS / GPS_L1_CA_CODE_LENGTH_CHIPS)));
unsigned int PRN = SV_ID;
channel_fsm_->Event_clear_test_result();
acquisition->stop_acquisition(); // reset the whole system including the sample counters
acquisition->init();
acquisition->set_local_code();
args.skip_used_samples = 0;
// Configure the DMA to send the required samples to perform an acquisition
args.nsamples_tx = nsamples_to_transfer;
// run the acquisition. The acquisition must run in a separate thread because it is a blocking function
args_acq.acquisition = acquisition;
if (pthread_create(&thread_acquisition, nullptr, handler_acquisition_acq_test, reinterpret_cast<void*>(&args_acq)) < 0)
{
std::cout << "ERROR cannot create acquisition Process" << std::endl;
}
if (start_msg == true)
{
//std::cout << "Reading external signal file: " << FLAGS_signal_file << std::endl;
std::cout << "Searching for " << System_and_Signal << " Satellites..." << std::endl;
std::cout << "[";
start_msg = false;
}
// wait to give time for the acquisition thread to set up the acquisition HW accelerator in the FPGA
usleep(1000000);
// create DMA child process
if (pthread_create(&thread_DMA, nullptr, handler_DMA_acq_test, reinterpret_cast<void*>(&args)) < 0)
{
std::cout << "ERROR cannot create DMA Process" << std::endl;
}
// wait until the acquisition is finished
pthread_join(thread_acquisition, nullptr);
// wait for the child DMA process to finish
pthread_join(thread_DMA, nullptr);
acquisition_successful = channel_fsm_->Event_check_test_result();
if (acquisition_successful)
{
std::cout << " " << PRN << " ";
gnss_synchro_vec.push_back(tmp_gnss_synchro);
}
else
{
std::cout << " . ";
}
std::cout.flush();
std::cout << "]" << std::endl;
std::cout << "-------------------------------------------\n";
for (auto& x : gnss_synchro_vec)
{
std::cout << "DETECTED SATELLITE " << System_and_Signal
<< " PRN: " << x.PRN
<< " with Doppler: " << x.Acq_doppler_hz
<< " [Hz], code phase: " << x.Acq_delay_samples
<< " [samples] at signal SampleStamp " << x.Acq_samplestamp_samples << "\n";
}
// report the elapsed time
end = std::chrono::system_clock::now();
elapsed_seconds = end - start;
std::cout << "Total signal acquisition run time "
<< elapsed_seconds.count()
<< " [seconds]" << std::endl;
if (!gnss_synchro_vec.empty())
{
return true;
}
else
{
return false;
}
}
void GpsL1CaPcpsAcquisitionTestFpga::init()
{
gnss_synchro.Channel_ID = 0;
@ -274,131 +486,55 @@ void GpsL1CaPcpsAcquisitionTestFpga::init()
signal.copy(gnss_synchro.Signal, 2, 0);
gnss_synchro.PRN = 1;
config->set_property("GNSS-SDR.internal_fs_sps", "4000000");
config->set_property("Acquisition_1C.implementation", "GPS_L1_CA_PCPS_Acquisition");
config->set_property("Acquisition_1C.item_type", "cshort");
config->set_property("Acquisition_1C.coherent_integration_time_ms", "1");
config->set_property("Acquisition_1C.dump", "false");
config->set_property("Acquisition_1C.threshold", "0.001");
config->set_property("Acquisition_1C.doppler_max", "5000");
config->set_property("Acquisition_1C.doppler_step", "500");
config->set_property("Acquisition_1C.repeat_satellite", "false");
config->set_property("Acquisition_1C.pfa", "0.0");
config->set_property("Acquisition_1C.select_queue_Fpga", "0");
config->set_property("Acquisition_1C.devicename", "/dev/uio0");
config->set_property("Acquisition.implementation", "GPS_L1_CA_PCPS_Acquisition_Fpga");
config->set_property("Acquisition.threshold", "0.00001");
config->set_property("Acquisition.doppler_max", std::to_string(doppler_max));
config->set_property("Acquisition.doppler_step", std::to_string(doppler_step));
config->set_property("Acquisition.repeat_satellite", "false");
// the test file is sampled @ 4MSPs only ,so we have to use the FPGA queue corresponding
// to the L5/E5a frequency band in order to avoid the L1/E1 factor :4 downsampling filter
config->set_property("Acquisition.downsampling_factor", "1");
config->set_property("Acquisition.select_queue_Fpga", "1");
}
TEST_F(GpsL1CaPcpsAcquisitionTestFpga, Instantiate)
{
init();
boost::shared_ptr<GpsL1CaPcpsAcquisitionFpga> acquisition =
boost::make_shared<GpsL1CaPcpsAcquisitionFpga>(config.get(), "Acquisition_1C", 0, 1);
}
TEST_F(GpsL1CaPcpsAcquisitionTestFpga, ValidationOfResults)
{
// pointer to the DMA thread that sends the samples to the acquisition engine
pthread_t thread_DMA;
struct DMA_handler_args_acq_test args;
std::chrono::time_point<std::chrono::system_clock> start, end;
std::chrono::duration<double> elapsed_seconds(0);
top_block = gr::make_top_block("Acquisition test");
double expected_delay_samples = 524;
double expected_doppler_hz = 1680;
init();
std::shared_ptr<GpsL1CaPcpsAcquisitionFpga> acquisition =
std::make_shared<GpsL1CaPcpsAcquisitionFpga>(config.get(), "Acquisition_1C", 0, 1);
start = std::chrono::system_clock::now();
boost::shared_ptr<GpsL1CaPcpsAcquisitionTestFpga_msg_rx> msg_rx = GpsL1CaPcpsAcquisitionTestFpga_msg_rx_make();
ASSERT_EQ(acquire_signal(), true);
ASSERT_NO_THROW(
{
acquisition->set_channel(1);
})
<< "Failure setting channel.";
end = std::chrono::system_clock::now();
elapsed_seconds = end - start;
ASSERT_NO_THROW(
{
acquisition->set_gnss_synchro(&gnss_synchro);
})
<< "Failure setting gnss_synchro.";
uint32_t n = 0; // there is only one channel
uint64_t nsamples = gnss_synchro_vec.at(n).Acq_samplestamp_samples;
std::cout << "Acquired " << nsamples_to_transfer << " samples in " << elapsed_seconds.count() * 1e6 << " microseconds" << std::endl;
ASSERT_NO_THROW(
{
acquisition->set_threshold(0.1);
})
<< "Failure setting threshold.";
double integration_period = static_cast<unsigned int>(std::round(static_cast<double>(GpsL1CaPcpsAcquisitionTestFpga::BASEBAND_SAMPLING_FREQ) / (GPS_L1_CA_CODE_RATE_CPS / GPS_L1_CA_CODE_LENGTH_CHIPS)));
ASSERT_NO_THROW(
{
acquisition->set_doppler_max(10000);
})
<< "Failure setting doppler_max.";
ASSERT_NO_THROW(
{
acquisition->set_doppler_step(250);
})
<< "Failure setting doppler_step.";
ASSERT_NO_THROW(
{
acquisition->connect(top_block);
})
<< "Failure connecting acquisition to the top_block.";
// uncomment the next line to load the file from the current directory
std::string file = "./GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
// uncomment the next two lines to load the file from the signal samples subdirectory
// std::string path = std::string(TEST_PATH);
// std::string file = path + "signal_samples/GPS_L1_CA_ID_1_Fs_4Msps_2ms.dat";
const char *file_name = file.c_str();
ASSERT_NO_THROW(
{
// for the unit test use dummy blocks to make the flowgraph work and allow the acquisition message to be sent.
// in the actual system there is a flowchart running in parallel so this is not needed
gr::blocks::file_source::sptr file_source = gr::blocks::file_source::make(sizeof(gr_complex), file_name, false);
gr::blocks::null_sink::sptr null_sink = gr::blocks::null_sink::make(sizeof(gr_complex));
gr::blocks::throttle::sptr throttle_block = gr::blocks::throttle::make(sizeof(gr_complex), 1000);
top_block->connect(file_source, 0, throttle_block, 0);
top_block->connect(throttle_block, 0, null_sink, 0);
top_block->msg_connect(acquisition->get_right_block(), pmt::mp("events"), msg_rx, pmt::mp("events"));
})
<< "Failure connecting the blocks of acquisition test.";
acquisition->set_state(1); // Ensure that acquisition starts at the first state
acquisition->init();
top_block->start(); // Start the top block
// start thread that sends the DMA samples to the FPGA
boost::thread t3{thread_acquisition_send_rx_samples, top_block, file_name};
EXPECT_NO_THROW(
{
start = std::chrono::system_clock::now();
acquisition->reset(); // launch the tracking process
top_block->wait();
end = std::chrono::system_clock::now();
elapsed_seconds = end - start;
})
<< "Failure running the top_block.";
t3.join();
std::cout << "Ran GpsL1CaPcpsAcquisitionTestFpga in " << elapsed_seconds.count() * 1e6
<< " microseconds" << std::endl;
ASSERT_EQ(1, msg_rx->rx_message) << "Acquisition failure. Expected message: 1=ACQ SUCCESS.";
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro.Acq_delay_samples);
double delay_error_samples = std::abs(expected_delay_samples - gnss_synchro_vec.at(n).Acq_delay_samples);
auto delay_error_chips = static_cast<float>(delay_error_samples * 1023 / 4000);
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro.Acq_doppler_hz);
double doppler_error_hz = std::abs(expected_doppler_hz - gnss_synchro_vec.at(n).Acq_doppler_hz);
// the acquisition grid is not available when using the FPGA
EXPECT_LE(doppler_error_hz, 666) << "Doppler error exceeds the expected value: 666 Hz = 2/(3*integration period)";
EXPECT_LT(delay_error_chips, 0.5) << "Delay error exceeds the expected value: 0.5 chips";
}