diff --git a/AUTHORS b/AUTHORS index aa7b0c2f5..4d7536e6c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -54,6 +54,7 @@ Gerald LaMountain gerald@gece.neu.edu Contributor Jim Melton jim.melton@sncorp.com Contributor Josh Schindehette jschindehette@geontech.com Contributor Leonardo Tonetto tonetto.dev@gmail.com Contributor +Malte Lenhart malte.lenhart@mailbox.org Contributor Mara Branzanti mara.branzanti@gmail.com Contributor Marc Molina marc.molina.pena@gmail.com Contributor Marc Sales marcsales92@gmail.com Contributor diff --git a/conf/gnss-sdr_GPS_L1_fifo.conf b/conf/gnss-sdr_GPS_L1_fifo.conf new file mode 100644 index 000000000..a339ffb35 --- /dev/null +++ b/conf/gnss-sdr_GPS_L1_fifo.conf @@ -0,0 +1,56 @@ +; This is a GNSS-SDR configuration file +; The configuration API is described at https://gnss-sdr.org/docs/sp-blocks/ +; SPDX-License-Identifier: GPL-3.0-or-later +; SPDX-FileCopyrightText: (C) 2010-2021 (see AUTHORS file for a list of contributors) + +; You can define your own receiver and invoke it by doing +; gnss-sdr --config_file=my_GNSS_SDR_configuration.conf +; + +[GNSS-SDR] + +;######### GLOBAL OPTIONS ################## +GNSS-SDR.internal_fs_sps=25000000 + +;######### SIGNAL_SOURCE CONFIG ############ +SignalSource.implementation=Fifo_Signal_Source +SignalSource.filename=fifo.fifo ; example usage: mkfifo fifo.fifo && cat path_to.bin >> fifo.fifo +SignalSource.sample_type=ishort; ; sample representation in fifo stream - will always output gr_complex +SignalSource.dump=false +;SignalSource.dump_filename=dump + +;######### SIGNAL_CONDITIONER CONFIG ############ +SignalConditioner.implementation=Pass_Through + +;######### CHANNELS GLOBAL CONFIG ############ +Channels_1C.count=8 +Channels.in_acquisition=1 +Channel.signal=1C + +;######### ACQUISITION GLOBAL CONFIG ############ +Acquisition_1C.implementation=GPS_L1_CA_PCPS_Acquisition +Acquisition_1C.item_type=gr_complex +Acquisition_1C.pfa=0.01 +Acquisition_1C.doppler_max=10000 +Acquisition_1C.doppler_step=250 + +;######### TRACKING GLOBAL CONFIG ############ +Tracking_1C.implementation=GPS_L1_CA_DLL_PLL_Tracking +Tracking_1C.item_type=gr_complex +Tracking_1C.pll_bw_hz=40.0; +Tracking_1C.dll_bw_hz=4.0; + +;######### TELEMETRY DECODER GPS CONFIG ############ +TelemetryDecoder_1C.implementation=GPS_L1_CA_Telemetry_Decoder + +;######### OBSERVABLES CONFIG ############ +Observables.implementation=Hybrid_Observables + +;######### PVT CONFIG ############ +PVT.implementation=RTKLIB_PVT +PVT.positioning_mode=Single +PVT.output_rate_ms=100 +PVT.display_rate_ms=500 +PVT.iono_model=Broadcast +PVT.trop_model=Saastamoinen +PVT.output_path=./files diff --git a/docs/changelog.md b/docs/changelog.md index b814caef2..aca552202 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -62,6 +62,11 @@ SPDX-FileCopyrightText: 2011-2021 Carles Fernandez-Prades +#include +#include + + +using namespace std::string_literals; + +FifoSignalSource::FifoSignalSource(ConfigurationInterface const* configuration, + std::string const& role, unsigned int in_streams, unsigned int out_streams, + [[maybe_unused]] Concurrent_Queue* queue) + : SignalSourceBase(configuration, role, "Fifo_Signal_Source"s), + item_size_(sizeof(gr_complex)), // currenty output item size is always gr_complex + fifo_reader_(FifoReader::make(configuration->property(role + ".filename"s, "../data/example_capture.dat"s), + configuration->property(role + ".sample_type"s, "ishort"s))), + dump_(configuration->property(role + ".dump", false)), + dump_filename_(configuration->property(role + ".dump_filename"s, "./data/signal_source.dat"s)) +{ + if (dump_) + { + DLOG(INFO) << "Dumping output into file " << (dump_filename_ + ".bin"s); + file_sink_ = gr::blocks::file_sink::make(item_size_, (dump_filename_ + ".bin").c_str()); + } + + if (in_streams > 0) + { + LOG(ERROR) << "A signal source does not have an input stream"; + } + if (out_streams > 1) + { + LOG(ERROR) << "This implementation only supports one output stream"; + } +} + + +void FifoSignalSource::connect(gr::top_block_sptr top_block) +{ + // here we could add a throttle as done in the file_source_base if required + if (dump_) + { + top_block->connect(fifo_reader_, 0, file_sink_, 0); + DLOG(INFO) << "connected source to file sink"; + } +} + + +void FifoSignalSource::disconnect(gr::top_block_sptr top_block) +{ + if (dump_) + { + top_block->disconnect(fifo_reader_, 0, file_sink_, 0); + DLOG(INFO) << "disconnected source from file sink"; + } +} + + +size_t FifoSignalSource::item_size() +{ + return item_size_; +} + + +gr::basic_block_sptr FifoSignalSource::get_left_block() +{ + LOG(WARNING) << "Left block of a signal source should not be retrieved"; + return gr::blocks::file_source::sptr(); +} + + +gr::basic_block_sptr FifoSignalSource::get_right_block() +{ + return fifo_reader_; +} diff --git a/src/algorithms/signal_source/adapters/fifo_signal_source.h b/src/algorithms/signal_source/adapters/fifo_signal_source.h new file mode 100644 index 000000000..c3c83be9f --- /dev/null +++ b/src/algorithms/signal_source/adapters/fifo_signal_source.h @@ -0,0 +1,79 @@ +/*! + * \file fifo_signal_source.h + * + * \brief Header file of the class for retrieving samples through a Unix FIFO + * \author Malte Lenhart, 2021. malte.lenhart(at)mailbox.org + * + * + * ----------------------------------------------------------------------------- + * + * 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) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_FIFO_SIGNAL_SOURCE_H +#define GNSS_SDR_FIFO_SIGNAL_SOURCE_H + +#include "concurrent_queue.h" +#include "signal_source_base.h" +#include + + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_adapters + * \{ */ + +// forward declaration to avoid include in header +class ConfigurationInterface; + +//! \brief Class that reads a sample stream from a Unix FIFO. +//! +//! This class supports the following properties: +//! +//! .filename - the path to the input file +//! - may be overridden by the -signal_source or -s command-line arguments +//! +//! .sample_type - data type read out from the FIFO. default ishort ; +//! - note: not output format. that is always gr_complex +//! +//! .dump - whether to archive input data +//! +//! .dump_filename - if dumping, path to file for output +//! +class FifoSignalSource : public SignalSourceBase +{ +public: + FifoSignalSource(const ConfigurationInterface* configuration, const std::string& role, + unsigned int in_streams, unsigned int out_streams, + Concurrent_Queue* queue); + + ~FifoSignalSource() = default; + + //! override methods from GNSSBlockInterface + void connect(gr::top_block_sptr top_block) override; + void disconnect(gr::top_block_sptr top_block) override; + size_t item_size() override; + gr::basic_block_sptr get_left_block() override; + gr::basic_block_sptr get_right_block() override; + +protected: +private: + //! output size - always gr_complex + const size_t item_size_; + //! internal fifo_reader_ class acts as signal source + const gnss_shared_ptr fifo_reader_; + + gnss_shared_ptr file_sink_; + const bool dump_; + const std::string dump_filename_; +}; + +/** \} */ +/** \} */ +#endif // GNSS_SDR_FIFO_SIGNAL_SOURCE_H diff --git a/src/algorithms/signal_source/adapters/file_source_base.cc b/src/algorithms/signal_source/adapters/file_source_base.cc index cdacaac5d..72758309a 100644 --- a/src/algorithms/signal_source/adapters/file_source_base.cc +++ b/src/algorithms/signal_source/adapters/file_source_base.cc @@ -261,7 +261,7 @@ uint64_t FileSourceBase::samples() const std::tuple FileSourceBase::itemTypeToSize() { - auto is_complex_t = false; + auto is_interleaved = false; auto item_size = size_t(0); if (item_type_ == "gr_complex") @@ -279,7 +279,7 @@ std::tuple FileSourceBase::itemTypeToSize() else if (item_type_ == "ishort") { item_size = sizeof(int16_t); - is_complex_t = true; + is_interleaved = true; } else if (item_type_ == "byte") { @@ -288,7 +288,7 @@ std::tuple FileSourceBase::itemTypeToSize() else if (item_type_ == "ibyte") { item_size = sizeof(int8_t); - is_complex_t = true; + is_interleaved = true; } else { @@ -297,7 +297,7 @@ std::tuple FileSourceBase::itemTypeToSize() item_size = sizeof(gr_complex); } - return std::make_tuple(item_size, is_complex_t); + return std::make_tuple(item_size, is_interleaved); } @@ -338,7 +338,7 @@ size_t FileSourceBase::samplesToSkip() const size_t FileSourceBase::computeSamplesInFile() const { - auto n_samples = size_t(samples()); + auto n_samples = static_cast(samples()); // if configured with 0 samples (read the whole file), figure out how many samples are in the file, and go from there if (n_samples == 0) @@ -467,7 +467,7 @@ gnss_shared_ptr FileSourceBase::create_valve() if (samples() > 0) { // if a number of samples is specified, honor it by creating a valve - // In practice, this is always true + // in practice, this is always true valve_ = gnss_sdr_make_valve(source_item_size(), samples(), queue_); DLOG(INFO) << "valve(" << valve_->unique_id() << ")"; @@ -499,7 +499,7 @@ void FileSourceBase::create_valve_hook() {} void FileSourceBase::create_sink_hook() {} -// Subclass hooks for connection/disconnectino +// Subclass hooks for connection/disconnection void FileSourceBase::pre_connect_hook(gr::top_block_sptr top_block [[maybe_unused]]) {} // NOLINT(performance-unnecessary-value-param) void FileSourceBase::post_connect_hook(gr::top_block_sptr top_block [[maybe_unused]]) {} // NOLINT(performance-unnecessary-value-param) void FileSourceBase::pre_disconnect_hook(gr::top_block_sptr top_block [[maybe_unused]]) {} // NOLINT(performance-unnecessary-value-param) diff --git a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt index cc88f2607..96fa168c2 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt +++ b/src/algorithms/signal_source/gnuradio_blocks/CMakeLists.txt @@ -14,6 +14,7 @@ endif() set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES + fifo_reader.cc unpack_byte_2bit_samples.cc unpack_byte_2bit_cpx_samples.cc unpack_byte_4bit_samples.cc @@ -27,6 +28,7 @@ set(SIGNAL_SOURCE_GR_BLOCKS_SOURCES set(SIGNAL_SOURCE_GR_BLOCKS_HEADERS + fifo_reader.h unpack_byte_2bit_samples.h unpack_byte_2bit_cpx_samples.h unpack_byte_4bit_samples.h diff --git a/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.cc b/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.cc new file mode 100644 index 000000000..4eb7d53cf --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.cc @@ -0,0 +1,114 @@ +/*! + * \file fifo_reader.cc + * + * \brief Implementation of the class to retrieve samples from an existing Unix FIFO + * \author Malte Lenhart, 2021. malte.lenhart(at)mailbox.org + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#include "fifo_reader.h" +#include + +// initial construction; pass to private constructor +FifoReader::sptr FifoReader::make(const std::string &file_name, const std::string &sample_type) +{ + return gnuradio::get_initial_sptr(new FifoReader(file_name, sample_type)); +} + +// private constructor called by ::make +FifoReader::FifoReader(const std::string &file_name, const std::string &sample_type) + : gr::sync_block("fifo_reader", + gr::io_signature::make(0, 0, 0), // no input + gr::io_signature::make(1, 1, sizeof(gr_complex))), // <+MIN_OUT+>, <+MAX_OUT+>, sizeof(<+OTYPE+>) + file_name_(file_name), + sample_type_(sample_type) +{ + DLOG(INFO) << "Starting FifoReader"; +} + +bool FifoReader::start() +{ + fifo_.open(file_name_, std::ios::binary); + if (!fifo_.is_open()) + { + LOG(ERROR) << "Error opening FIFO"; + return false; + } + return true; +} + +// work loop +// taken from here: https://stackoverflow.com/questions/25546619/work-with-fifo-in-c-blocking-read +int FifoReader::work(int noutput_items, + __attribute__((unused)) gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + if (output_items.size() > 1) + { + LOG(ERROR) << "FifoReader connected to too many outputs"; + } + + // read samples out + size_t items_retrieved = 0; + if (sample_type_ == "ishort") + { + // ishort == int16_t + items_retrieved = read_interleaved(noutput_items, output_items); + } + else if (sample_type_ == "gr_complex") + { + LOG(WARNING) << sample_type_ << " is not yet tested. Please consider removing this warning if tested successfully"; + items_retrieved = read_gr_complex(noutput_items, output_items); + } + else + { + // please see gr_complex_ip_packet_source for inspiration on how to implement other sample types + LOG(ERROR) << sample_type_ << " is unfortunately not yet implemented as sample type"; + } + + // we return varying number of data -> call produce & return flag + produce(0, items_retrieved); + return this->WORK_CALLED_PRODUCE; +} + +// read gr_complex items from fifo +// this fct has duplicate code with the templated read_interleaved fct in header +size_t FifoReader::read_gr_complex(int noutput_items, gr_vector_void_star &output_items) +{ + size_t items_retrieved = 0; + for (int n = 0; n < noutput_items; n++) + { + gr_complex sample; + fifo_.read(reinterpret_cast(&sample), sizeof(sample)); + if (fifo_.good()) + { + static_cast(output_items.at(0))[n] = sample; + items_retrieved++; + } + else if (fifo_.eof()) + { + fifo_.clear(); + break; + } + else + { + fifo_error_output(); + break; + } + } + return items_retrieved; +} + +void FifoReader::fifo_error_output() const +{ + LOG(ERROR) << "unhandled FIFO event"; +} diff --git a/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.h b/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.h new file mode 100644 index 000000000..3d4e0c520 --- /dev/null +++ b/src/algorithms/signal_source/gnuradio_blocks/fifo_reader.h @@ -0,0 +1,99 @@ +/*! + * \file fifo_reader.h + * + * \brief Header file to retrieve samples from an existing Unix FIFO + * \author Malte Lenhart, 2021. malte.lenhart(at)mailbox.org + * + * ----------------------------------------------------------------------------- + * + * GNSS-SDR is a Global Navigation Satellite System software-defined receiver. + * This file is part of GNSS-SDR. + * + * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * ----------------------------------------------------------------------------- + */ + +#ifndef GNSS_SDR_FIFO_READER_H_ +#define GNSS_SDR_FIFO_READER_H_ + +#include "gnss_block_interface.h" +#include +#include // std::ifstream + +/** \addtogroup Signal_Source + * \{ */ +/** \addtogroup Signal_Source_gnuradio_blocks + * \{ */ +class FifoReader : virtual public gr::sync_block +{ +public: + //! \brief static function to create a class instance + using sptr = gnss_shared_ptr; + static sptr make(const std::string &file_name, const std::string &sample_type); + + ~FifoReader() = default; + + //! initialize istream resource for FIFO + bool start(); + + // gnu radio work cycle function + int work(int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + +private: + //! \brief Constructor + //! private constructor called by function make + //! (gr handles this with public and private header pair) + FifoReader(const std::string &file_name, const std::string &sample_type); + + size_t read_gr_complex(int noutput_items, gr_vector_void_star &output_items); + //! function to read data out of FIFO which is stored as interleaved I/Q stream. + //! template argument determines sample_type + template + size_t read_interleaved(int noutput_items, gr_vector_void_star &output_items) + { + size_t items_retrieved = 0; + for (int n = 0; n < noutput_items; n++) + { + // TODO: try if performance increases if we copy larger chunks to vector. + // how to read from stream: https://en.cppreference.com/w/cpp/io/basic_ifstream + std::array buffer; // gr_complex is 32bit = 4*char + fifo_.read(reinterpret_cast(&buffer[0]), buffer.size()); + if (fifo_.good()) + { + Type real; + Type imag; + memcpy(&real, &buffer[0], sizeof(real)); + memcpy(&imag, &buffer[2], sizeof(imag)); + static_cast(output_items.at(0))[n] = gr_complex(real, imag); + items_retrieved++; + } + else if (fifo_.eof()) + { + fifo_.clear(); + break; + } + else + { + fifo_error_output(); + break; + } + } + return items_retrieved; + } + + //! this function moves logging output from this header into the source file + //! thereby eliminating the need to include glog/logging.h in this header + void fifo_error_output() const; + + const std::string file_name_; + const std::string sample_type_; + std::ifstream fifo_; +}; + +/** \} */ +/** \} */ +#endif /* GNSS_SDR_FIFO_READER_H_ */ diff --git a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc index 6d43166a7..0269dfd33 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/gr_complex_ip_packet_source.cc @@ -104,6 +104,7 @@ Gr_Complex_Ip_Packet_Source::Gr_Complex_Ip_Packet_Source(std::string src_device, gr::io_signature::make(1, 4, item_size)) // 1 to 4 baseband complex channels { std::cout << "Start Ethernet packet capture\n"; + std::cout << "Overflow events will be indicated by o's\n"; d_n_baseband_channels = n_baseband_channels; if (wire_sample_type == "cbyte") @@ -121,6 +122,11 @@ Gr_Complex_Ip_Packet_Source::Gr_Complex_Ip_Packet_Source(std::string src_device, d_wire_sample_type = 3; d_bytes_per_sample = d_n_baseband_channels * 8; } + else if (wire_sample_type == "ishort") + { + d_wire_sample_type = 4; + d_bytes_per_sample = d_n_baseband_channels * 4; + } else { std::cout << "Unknown wire sample type\n"; @@ -307,7 +313,7 @@ void Gr_Complex_Ip_Packet_Source::pcap_callback(__attribute__((unused)) u_char * else { // notify overflow - std::cout << "O" << std::flush; + std::cout << "o" << std::flush; } } } @@ -397,6 +403,25 @@ void Gr_Complex_Ip_Packet_Source::demux_samples(const gr_vector_void_star &outpu } } break; + case 4: // interleaved short samples + for (const auto &output_item : output_items) + { + int16_t real; + int16_t imag; + memcpy(&real, &fifo_buff[fifo_read_ptr], sizeof(real)); + fifo_read_ptr += 2; // two bytes in short + memcpy(&imag, &fifo_buff[fifo_read_ptr], sizeof(imag)); + fifo_read_ptr += 2; // two bytes in short + if (d_IQ_swap) + { + static_cast(output_item)[n] = gr_complex(real, imag); + } + else + { + static_cast(output_item)[n] = gr_complex(imag, real); + } + } + break; default: std::cout << "Unknown wire sample type\n"; exit(0); @@ -422,36 +447,20 @@ int Gr_Complex_Ip_Packet_Source::work(int noutput_items, if (output_items.size() > static_cast(d_n_baseband_channels)) { - std::cout << "Configuration error: more baseband channels connected than the available in the UDP source\n"; + std::cout << "Configuration error: more baseband channels connected than available in the UDP source\n"; exit(0); } int num_samples_readed; int bytes_requested; - switch (d_wire_sample_type) + + bytes_requested = noutput_items * d_bytes_per_sample; + if (bytes_requested < fifo_items) { - case 1: // complex byte samples - case 2: // complex 4 bits samples - case 3: // complex float samples - bytes_requested = noutput_items * d_bytes_per_sample; - if (bytes_requested < fifo_items) - { - num_samples_readed = noutput_items; // read all - } - else - { - num_samples_readed = fifo_items / d_bytes_per_sample; // read what we have - } - break; - default: // complex byte samples - bytes_requested = noutput_items * d_bytes_per_sample; - if (bytes_requested < fifo_items) - { - num_samples_readed = noutput_items; // read all - } - else - { - num_samples_readed = fifo_items / d_bytes_per_sample; // read what we have - } + num_samples_readed = noutput_items; // read all + } + else + { + num_samples_readed = fifo_items / d_bytes_per_sample; // read what we have } bytes_requested = num_samples_readed * d_bytes_per_sample; diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index 0085b9612..b99f5a686 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -37,6 +37,7 @@ #include "channel.h" #include "configuration_interface.h" #include "direct_resampler_conditioner.h" +#include "fifo_signal_source.h" #include "file_signal_source.h" #include "fir_filter.h" #include "freq_xlating_fir_filter.h" @@ -647,6 +648,12 @@ std::unique_ptr GNSSBlockFactory::GetBlock( } // SIGNAL SOURCES ---------------------------------------------------------- + else if (implementation == "Fifo_Signal_Source") + { + std::unique_ptr block_ = std::make_unique(configuration, role, in_streams, + out_streams, queue); + block = std::move(block_); + } else if (implementation == "File_Signal_Source") { std::unique_ptr block_ = std::make_unique(configuration, role, in_streams,