diff --git a/src/algorithms/signal_source/adapters/file_signal_source.cc b/src/algorithms/signal_source/adapters/file_signal_source.cc index 6655c2743..3409b0abe 100644 --- a/src/algorithms/signal_source/adapters/file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/file_signal_source.cc @@ -24,7 +24,7 @@ using namespace std::string_literals; FileSignalSource::FileSignalSource(ConfigurationInterface const* configuration, std::string const& role, unsigned int in_streams, unsigned int out_streams, Concurrent_Queue* queue) - : FileSourceBase(configuration, role, "File_Signal_Source"s, queue) + : FileSourceBase(configuration, role, "File_Signal_Source"s, queue, "short"s) { if (in_streams > 0) { diff --git a/src/algorithms/signal_source/adapters/file_source_base.cc b/src/algorithms/signal_source/adapters/file_source_base.cc index ce8e19a8b..0adebc616 100644 --- a/src/algorithms/signal_source/adapters/file_source_base.cc +++ b/src/algorithms/signal_source/adapters/file_source_base.cc @@ -177,6 +177,7 @@ uint64_t FileSourceBase::samples() const void FileSourceBase::init() { create_file_source(); + // At this point, we know that the file exists samples_ = computeSamplesInFile(); auto signal_duration_s = 1.0 * samples_ / sampling_frequency_; @@ -196,6 +197,7 @@ void FileSourceBase::init() DLOG(INFO) << "Item type " << item_type_; DLOG(INFO) << "Item size " << item_size_; DLOG(INFO) << "Repeat " << repeat_; + DLOG(INFO) << "Dump " << dump_; DLOG(INFO) << "Dump filename " << dump_filename_; @@ -206,12 +208,13 @@ void FileSourceBase::init() FileSourceBase::FileSourceBase(ConfigurationInterface const* configuration, std::string role, std::string impl, - Concurrent_Queue* queue) + Concurrent_Queue* queue, + std::string default_item_type) : SignalSourceBase(configuration, role, impl) , filename_(configuration->property(role + ".filename"s, "../data/example_capture.dat"s)) , file_source_() - , item_type_(configuration->property(role + ".item_type"s, "short"s)) + , item_type_(configuration->property(role + ".item_type"s, default_item_type)) , item_size_(0) // invalid , is_complex_(false) @@ -283,6 +286,9 @@ std::tuple FileSourceBase::itemTypeToSize() const return std::make_tuple(item_size, is_complex); } +// Default case is one decoded packet per one read sample +double FileSourceBase::packetsPerSample() const { return 1.0; } + size_t FileSourceBase::samplesToSkip() const { auto samples_to_skip = size_t(0); @@ -310,32 +316,38 @@ size_t FileSourceBase::computeSamplesInFile() const auto n_samples = size_t(samples()); - // this could throw, but the existence of the file has been proven before we get here. - auto size = fs::file_size(filename()); - n_samples = std::floor(1.0 * size / item_size()); - - auto to_skip = samplesToSkip(); - - /*! - * BUG workaround: The GNU Radio file source does not stop the receiver after reaching the End of File. - * A possible solution is to compute the file length in samples using file size, excluding at least - * the last 2 milliseconds, and enable always the valve block - */ - auto tail = static_cast(std::ceil(0.002 * sampling_frequency())); - - DLOG(INFO) << "Total samples in the file= " << n_samples; - std::cout << "Processing file " << filename() << ", which contains " << n_samples << " samples (" << size << " bytes)\n"; - - if (n_samples > (to_skip + tail)) + // 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) { - // process all the samples available in the file excluding up to the last 2 ms - n_samples -= to_skip + tail; - } - else - { - // this will terminate the program - LOG(FATAL) << "Skipping " << to_skip << " samples from the front and truncating 2ms (" << tail << " samples)\n" - << "is greater than the number of samples in the file (" << n_samples << ")"; + // this could throw, but the existence of the file has been proven before we get here. + auto size = fs::file_size(filename()); + + // if there is some kind of compression/encoding, figure out the uncompressed number of samples + n_samples = std::floor(packetsPerSample() * size / item_size()); + + auto to_skip = samplesToSkip(); + + /*! + * BUG workaround: The GNU Radio file source does not stop the receiver after reaching the End of File. + * A possible solution is to compute the file length in samples using file size, excluding at least + * the last 2 milliseconds, and enable always the valve block + */ + auto tail = static_cast(std::ceil(0.002 * sampling_frequency())); + + DLOG(INFO) << "Total samples in the file= " << n_samples; + std::cout << "Processing file " << filename() << ", which contains " << n_samples << " samples (" << size << " bytes)\n"; + + if (n_samples > (to_skip + tail)) + { + // process all the samples available in the file excluding up to the last 2 ms + n_samples -= to_skip + tail; + } + else + { + // this will terminate the program + LOG(FATAL) << "Skipping " << to_skip << " samples from the front and truncating 2ms (" << tail << " samples)\n" + << "is greater than the number of samples in the file (" << n_samples << ")"; + } } return n_samples; diff --git a/src/algorithms/signal_source/adapters/file_source_base.h b/src/algorithms/signal_source/adapters/file_source_base.h index 2d7c400b3..7e7af4432 100644 --- a/src/algorithms/signal_source/adapters/file_source_base.h +++ b/src/algorithms/signal_source/adapters/file_source_base.h @@ -84,13 +84,20 @@ public: protected: //! Constructor + // Subclasses may want to assert default item types that are appropriate to the specific file + // type supported. Rather than require the item type to be specified in the config file, allow + // sub-classes to impose their will FileSourceBase(ConfigurationInterface const* configuration, std::string role, std::string impl, - Concurrent_Queue* queue); + Concurrent_Queue* queue, + std::string default_item_type="short"); //! compute the item size, from the item_type(). Subclasses may constrain types that don't make // sense. The return of this method is a tuple of item_size and is_complex virtual std::tuple itemTypeToSize() const; + //! the number of (possibly unpacked) samples in a (raw) file sample (default=1) + virtual double packetsPerSample() const; + //! compute the number of samples to skip virtual size_t samplesToSkip() const; diff --git a/src/algorithms/signal_source/adapters/nsr_file_signal_source.cc b/src/algorithms/signal_source/adapters/nsr_file_signal_source.cc index 8501a8df5..b957d785e 100644 --- a/src/algorithms/signal_source/adapters/nsr_file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/nsr_file_signal_source.cc @@ -10,289 +10,68 @@ * 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) + * Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors) * SPDX-License-Identifier: GPL-3.0-or-later * * ----------------------------------------------------------------------------- */ #include "nsr_file_signal_source.h" -#include "configuration_interface.h" -#include "gnss_sdr_flags.h" -#include "gnss_sdr_valve.h" #include -#include -#include -#include -#include -#include using namespace std::string_literals; NsrFileSignalSource::NsrFileSignalSource(const ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams, Concurrent_Queue* queue) - : SignalSourceBase(configuration, role, "Nsr_File_Signal_Source"s) - , in_streams_(in_streams), out_streams_(out_streams) + : FileSourceBase(configuration, role, "Nsr_File_Signal_Source"s, queue, "byte"s) { - const std::string default_filename("../data/my_capture.dat"); - const std::string default_item_type("byte"); - const std::string default_dump_filename("../data/my_capture_dump.dat"); - - samples_ = configuration->property(role + ".samples", static_cast(0)); - sampling_frequency_ = configuration->property(role + ".sampling_frequency", static_cast(0)); - filename_ = configuration->property(role + ".filename", default_filename); - - // override value with commandline flag, if present - if (FLAGS_signal_source != "-") - { - filename_ = FLAGS_signal_source; - } - if (FLAGS_s != "-") - { - filename_ = FLAGS_s; - } - - item_type_ = configuration->property(role + ".item_type", default_item_type); - repeat_ = configuration->property(role + ".repeat", false); - dump_ = configuration->property(role + ".dump", false); - dump_filename_ = configuration->property(role + ".dump_filename", default_dump_filename); - enable_throttle_control_ = configuration->property(role + ".enable_throttle_control", false); - - if (item_type_ == "byte") - { - item_size_ = sizeof(char); - } - else - { - LOG(WARNING) << item_type_ << " unrecognized item type. Using byte."; - item_size_ = sizeof(char); - } - try - { - file_source_ = gr::blocks::file_source::make(item_size_, filename_.c_str(), repeat_); - unpack_byte_ = make_unpack_byte_2bit_samples(); - } - catch (const std::exception& e) - { - std::cerr - << "The receiver was configured to work with a file signal source\n" - << "but the specified file is unreachable by GNSS-SDR.\n" - << "Please modify your configuration file\n" - << "and point " << role << ".filename to a valid raw data file. Then:\n" - << "$ gnss-sdr --config_file=/path/to/my_GNSS_SDR_configuration.conf\n" - << "Examples of configuration files available at:\n" - << GNSSSDR_INSTALL_DIR "/share/gnss-sdr/conf/\n"; - - LOG(WARNING) << "nsr_file_signal_source: Unable to open the samples file " - << filename_ << ", exiting the program."; - throw; - } - - DLOG(INFO) << "file_source(" << file_source_->unique_id() << ")"; - - if (samples_ == 0) // read all file - { - /*! - * BUG workaround: The GNU Radio file source does not stop the receiver after reaching the End of File. - * A possible solution is to compute the file length in samples using file size, excluding the last 2 milliseconds, and enable always the - * valve block - */ - std::ifstream file(filename_.c_str(), std::ios::in | std::ios::binary | std::ios::ate); - std::ifstream::pos_type size; - - if (file.is_open()) - { - size = file.tellg(); - LOG(INFO) << "Total samples in the file= " << floor(static_cast(size) / static_cast(item_size())); - } - else - { - std::cout << "file_signal_source: Unable to open the samples file " << filename_.c_str() << '\n'; - LOG(ERROR) << "file_signal_source: Unable to open the samples file " << filename_.c_str(); - } - std::streamsize ss = std::cout.precision(); - std::cout << std::setprecision(16); - std::cout << "Processing file " << filename_ << ", which contains " << size << " [bytes]\n"; - std::cout.precision(ss); - - if (size > 0) - { - int sample_packet_factor = 4; // 1 byte -> 4 samples - samples_ = floor(static_cast(size) / static_cast(item_size())) * sample_packet_factor; - samples_ = samples_ - ceil(0.002 * static_cast(sampling_frequency_)); // process all the samples available in the file excluding the last 2 ms - } - } - - CHECK(samples_ > 0) << "File does not contain enough samples to process."; - const double signal_duration_s = static_cast(samples_) * (1 / static_cast(sampling_frequency_)); - LOG(INFO) << "Total number samples to be processed= " << samples_ << " GNSS signal duration= " << signal_duration_s << " [s]"; - std::cout << "GNSS signal recorded time to be processed: " << signal_duration_s << " [s]\n"; - - valve_ = gnss_sdr_make_valve(sizeof(float), samples_, queue); - DLOG(INFO) << "valve(" << valve_->unique_id() << ")"; - - if (dump_) - { - // sink_ = gr_make_file_sink(item_size_, dump_filename_.c_str()); - sink_ = gr::blocks::file_sink::make(sizeof(float), dump_filename_.c_str()); - DLOG(INFO) << "file_sink(" << sink_->unique_id() << ")"; - } - - if (enable_throttle_control_) - { - throttle_ = gr::blocks::throttle::make(sizeof(float), sampling_frequency_); - } - DLOG(INFO) << "File source filename " << filename_; - DLOG(INFO) << "Samples " << samples_; - DLOG(INFO) << "Sampling frequency " << sampling_frequency_; - DLOG(INFO) << "Item type " << item_type_; - DLOG(INFO) << "Item size " << item_size_; - DLOG(INFO) << "Repeat " << repeat_; - DLOG(INFO) << "Dump " << dump_; - DLOG(INFO) << "Dump filename " << dump_filename_; - if (in_streams_ > 0) + if (in_streams > 0) { LOG(ERROR) << "A signal source does not have an input stream"; } - if (out_streams_ > 1) + if (out_streams > 1) { LOG(ERROR) << "This implementation only supports one output stream"; } } - -void NsrFileSignalSource::connect(gr::top_block_sptr top_block) +std::tuple NsrFileSignalSource::itemTypeToSize() const { - if (samples_ > 0) + auto is_complex = false; + auto item_size = size_t(sizeof(char)); // default + + if (item_type() == "byte") { - if (enable_throttle_control_ == true) - { - top_block->connect(file_source_, 0, unpack_byte_, 0); - top_block->connect(unpack_byte_, 0, throttle_, 0); - DLOG(INFO) << "connected file source to throttle"; - top_block->connect(throttle_, 0, valve_, 0); - DLOG(INFO) << "connected throttle to valve"; - if (dump_) - { - top_block->connect(valve_, 0, sink_, 0); - DLOG(INFO) << "connected valve to file sink"; - } - } - else - { - top_block->connect(file_source_, 0, unpack_byte_, 0); - top_block->connect(unpack_byte_, 0, valve_, 0); - DLOG(INFO) << "connected file source to valve"; - if (dump_) - { - top_block->connect(valve_, 0, sink_, 0); - DLOG(INFO) << "connected valve to file sink"; - } - } + item_size = sizeof(char); } else { - if (enable_throttle_control_ == true) - { - top_block->connect(file_source_, 0, unpack_byte_, 0); - top_block->connect(unpack_byte_, 0, throttle_, 0); - DLOG(INFO) << "connected file source to throttle"; - if (dump_) - { - top_block->connect(throttle_, 0, sink_, 0); - DLOG(INFO) << "connected file source to sink"; - } - } - else - { - if (dump_) - { - top_block->connect(file_source_, 0, unpack_byte_, 0); - top_block->connect(unpack_byte_, 0, sink_, 0); - DLOG(INFO) << "connected file source to sink"; - } - } + LOG(WARNING) << item_type() << " unrecognized item type. Using byte."; } + + return std::make_tuple(item_size, is_complex); } +// 1 byte -> 4 samples +double NsrFileSignalSource::packetsPerSample() const { return 4.0; } +gnss_shared_ptr NsrFileSignalSource::source() const { return unpack_byte_; } -void NsrFileSignalSource::disconnect(gr::top_block_sptr top_block) + +void NsrFileSignalSource::create_file_source_hook() { - if (samples_ > 0) - { - if (enable_throttle_control_ == true) - { - top_block->disconnect(file_source_, 0, unpack_byte_, 0); - DLOG(INFO) << "disconnected file source to unpack_byte_"; - top_block->connect(unpack_byte_, 0, throttle_, 0); - DLOG(INFO) << "disconnected unpack_byte_ to throttle_"; - top_block->disconnect(throttle_, 0, valve_, 0); - DLOG(INFO) << "disconnected throttle to valve"; - if (dump_) - { - top_block->disconnect(valve_, 0, sink_, 0); - DLOG(INFO) << "disconnected valve to file sink"; - } - } - else - { - top_block->disconnect(file_source_, 0, unpack_byte_, 0); - DLOG(INFO) << "disconnected file source to unpack_byte_"; - top_block->disconnect(unpack_byte_, 0, valve_, 0); - DLOG(INFO) << "disconnected unpack_byte_ to valve"; - if (dump_) - { - top_block->disconnect(valve_, 0, sink_, 0); - DLOG(INFO) << "disconnected valve to file sink"; - } - } - } - else - { - if (enable_throttle_control_ == true) - { - top_block->disconnect(file_source_, 0, unpack_byte_, 0); - DLOG(INFO) << "disconnected file source to unpack_byte_"; - top_block->disconnect(unpack_byte_, 0, throttle_, 0); - DLOG(INFO) << "disconnected unpack_byte_ to throttle"; - if (dump_) - { - top_block->disconnect(unpack_byte_, 0, sink_, 0); - DLOG(INFO) << "disconnected funpack_byte_ to sink"; - } - } - else - { - if (dump_) - { - top_block->disconnect(file_source_, 0, unpack_byte_, 0); - DLOG(INFO) << "disconnected file source to unpack_byte_"; - top_block->disconnect(unpack_byte_, 0, sink_, 0); - DLOG(INFO) << "disconnected unpack_byte_ to sink"; - } - } - } + unpack_byte_ = make_unpack_byte_2bit_samples(); + DLOG(INFO) << "unpack_byte_2bit_samples(" << unpack_byte_->unique_id() << ")"; } - -gr::basic_block_sptr NsrFileSignalSource::get_left_block() +void NsrFileSignalSource::pre_connect_hook(gr::top_block_sptr top_block) { - LOG(WARNING) << "Left block of a signal source should not be retrieved"; - // return gr_block_sptr(); - return gr::blocks::file_source::sptr(); + top_block->connect(file_source(), 0, unpack_byte_, 0); + DLOG(INFO) << "connected file_source to unpacker"; } - -gr::basic_block_sptr NsrFileSignalSource::get_right_block() +void NsrFileSignalSource::pre_disconnect_hook(gr::top_block_sptr top_block) { - if (samples_ > 0) - { - return valve_; - } - if (enable_throttle_control_ == true) - { - return throttle_; - } - return unpack_byte_; + top_block->disconnect(file_source(), 0, unpack_byte_, 0); + DLOG(INFO) << "disconnected file_source from unpacker"; } diff --git a/src/algorithms/signal_source/adapters/nsr_file_signal_source.h b/src/algorithms/signal_source/adapters/nsr_file_signal_source.h index d3dbabe71..3a6455aa4 100644 --- a/src/algorithms/signal_source/adapters/nsr_file_signal_source.h +++ b/src/algorithms/signal_source/adapters/nsr_file_signal_source.h @@ -12,7 +12,7 @@ * 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) + * Copyright (C) 2010-2021 (see AUTHORS file for a list of contributors) * SPDX-License-Identifier: GPL-3.0-or-later * * ----------------------------------------------------------------------------- @@ -21,17 +21,9 @@ #ifndef GNSS_SDR_NSR_FILE_SIGNAL_SOURCE_H #define GNSS_SDR_NSR_FILE_SIGNAL_SOURCE_H -#include "signal_source_base.h" +#include "file_source_base.h" -#include "concurrent_queue.h" -#include "gnss_block_interface.h" #include "unpack_byte_2bit_samples.h" -#include -#include -#include -#include -#include -#include /** \addtogroup Signal_Source * \{ */ @@ -44,7 +36,7 @@ class ConfigurationInterface; * \brief Class that reads signals samples from a file * and adapts it to a SignalSourceInterface */ -class NsrFileSignalSource : public SignalSourceBase +class NsrFileSignalSource : public FileSourceBase { public: NsrFileSignalSource(const ConfigurationInterface* configuration, const std::string& role, @@ -53,59 +45,16 @@ public: ~NsrFileSignalSource() = default; - inline size_t item_size() override - { - return item_size_; - } - - void connect(gr::top_block_sptr top_block) override; - void disconnect(gr::top_block_sptr top_block) override; - gr::basic_block_sptr get_left_block() override; - gr::basic_block_sptr get_right_block() override; - - inline std::string filename() const - { - return filename_; - } - - inline std::string item_type() const - { - return item_type_; - } - - inline bool repeat() const - { - return repeat_; - } - - inline int64_t sampling_frequency() const - { - return sampling_frequency_; - } - - inline uint64_t samples() const - { - return samples_; - } +protected: + std::tuple itemTypeToSize() const override; + double packetsPerSample() const override; + gnss_shared_ptr source() const override; + void create_file_source_hook() override; + void pre_connect_hook(gr::top_block_sptr top_block) override; + void pre_disconnect_hook(gr::top_block_sptr top_block) override; private: - gr::blocks::file_source::sptr file_source_; unpack_byte_2bit_samples_sptr unpack_byte_; - gnss_shared_ptr valve_; - gr::blocks::file_sink::sptr sink_; - gr::blocks::throttle::sptr throttle_; - uint64_t samples_; - int64_t sampling_frequency_; - size_t item_size_; - std::string filename_; - std::string item_type_; - std::string dump_filename_; - uint32_t in_streams_; - uint32_t out_streams_; - bool repeat_; - bool dump_; - // Throttle control - bool enable_throttle_control_; }; diff --git a/src/algorithms/signal_source/adapters/spir_file_signal_source.cc b/src/algorithms/signal_source/adapters/spir_file_signal_source.cc index 7c00100de..e9a93dedd 100644 --- a/src/algorithms/signal_source/adapters/spir_file_signal_source.cc +++ b/src/algorithms/signal_source/adapters/spir_file_signal_source.cc @@ -24,7 +24,7 @@ using namespace std::string_literals; SpirFileSignalSource::SpirFileSignalSource(const ConfigurationInterface* configuration, const std::string& role, unsigned int in_streams, unsigned int out_streams, Concurrent_Queue* queue) - : FileSourceBase(configuration, role, "Spir_File_Signal_Source"s, queue) + : FileSourceBase(configuration, role, "Spir_File_Signal_Source"s, queue, "int"s) { if (in_streams > 0) { diff --git a/src/algorithms/signal_source/adapters/spir_file_signal_source.h b/src/algorithms/signal_source/adapters/spir_file_signal_source.h index 7694579de..e8f452229 100644 --- a/src/algorithms/signal_source/adapters/spir_file_signal_source.h +++ b/src/algorithms/signal_source/adapters/spir_file_signal_source.h @@ -19,7 +19,6 @@ #define GNSS_SDR_SPIR_FILE_SIGNAL_SOURCE_H #include "file_source_base.h" - #include "unpack_intspir_1bit_samples.h" @@ -45,12 +44,11 @@ public: ~SpirFileSignalSource() = default; protected: -std::tuple itemTypeToSize() const override; - gnss_shared_ptr source() const override; - void create_file_source_hook() override; - void pre_connect_hook(gr::top_block_sptr top_block) override; - void post_disconnect_hook(gr::top_block_sptr top_block) override; - + std::tuple itemTypeToSize() const override; + gnss_shared_ptr source() const override; + void create_file_source_hook() override; + void pre_connect_hook(gr::top_block_sptr top_block) override; + void post_disconnect_hook(gr::top_block_sptr top_block) override; private: