diff --git a/docs/changelog.md b/docs/changelog.md index aed239a7b..8300fc858 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -28,6 +28,15 @@ SPDX-FileCopyrightText: 2011-2021 Carles Fernandez-Prades +#include #include @@ -40,6 +41,18 @@ void SignalConditioner::connect(gr::top_block_sptr top_block) LOG(WARNING) << "Signal conditioner already connected internally"; return; } + if (data_type_adapt_ == nullptr) + { + throw std::invalid_argument("DataTypeAdapter implementation not defined"); + } + if (in_filt_ == nullptr) + { + throw std::invalid_argument("InputFilter implementation not defined"); + } + if (res_ == nullptr) + { + throw std::invalid_argument("Resampler implementation not defined"); + } data_type_adapt_->connect(top_block); in_filt_->connect(top_block); res_->connect(top_block); diff --git a/src/core/receiver/control_thread.cc b/src/core/receiver/control_thread.cc index e0642f15c..5ab055c9a 100644 --- a/src/core/receiver/control_thread.cc +++ b/src/core/receiver/control_thread.cc @@ -300,7 +300,6 @@ int ControlThread::run() } else { - LOG(ERROR) << "Unable to connect flowgraph"; return 0; } // Start the flowgraph @@ -311,7 +310,6 @@ int ControlThread::run() } else { - LOG(ERROR) << "Unable to start flowgraph"; return 0; } diff --git a/src/core/receiver/gnss_block_factory.cc b/src/core/receiver/gnss_block_factory.cc index 8e30d9caf..67ab07687 100644 --- a/src/core/receiver/gnss_block_factory.cc +++ b/src/core/receiver/gnss_block_factory.cc @@ -374,6 +374,7 @@ std::unique_ptr GNSSBlockFactory::GetChannel( if (acq_item_type != trk_item_type) { LOG(ERROR) << "Acquisition and Tracking blocks must have the same input data type!"; + return nullptr; } LOG(INFO) << "Instantiating Channel " << channel @@ -388,6 +389,11 @@ std::unique_ptr GNSSBlockFactory::GetChannel( std::unique_ptr trk_ = GetTrkBlock(configuration, "Tracking_" + signal + appendix2, 1, 1); std::unique_ptr tlm_ = GetTlmBlock(configuration, "TelemetryDecoder_" + signal + appendix3, 1, 1); + if (acq_ == nullptr or trk_ == nullptr or tlm_ == nullptr) + { + return nullptr; + } + std::unique_ptr channel_ = std::make_unique(configuration, channel, std::move(acq_), std::move(trk_), @@ -1320,8 +1326,7 @@ std::unique_ptr GNSSBlockFactory::GetBlock( else { - // Log fatal. This causes execution to stop. - LOG(ERROR) << role << "." << implementation << ": Undefined implementation for block"; + LOG(ERROR) << role << " block: Undefined implementation " << implementation; } return block; } @@ -1515,8 +1520,7 @@ std::unique_ptr GNSSBlockFactory::GetAcqBlock( else { - // Log fatal. This causes execution to stop. - LOG(ERROR) << role << "." << implementation << ": Undefined implementation for block"; + LOG(ERROR) << role << " block: Undefined implementation " << implementation; } return block; } @@ -1671,8 +1675,7 @@ std::unique_ptr GNSSBlockFactory::GetTrkBlock( #endif else { - // Log fatal. This causes execution to stop. - LOG(ERROR) << role << "." << implementation << ": Undefined implementation for block"; + LOG(ERROR) << role << " block: Undefined implementation " << implementation; } return block; } @@ -1764,8 +1767,7 @@ std::unique_ptr GNSSBlockFactory::GetTlmBlock( else { - // Log fatal. This causes execution to stop. - LOG(ERROR) << role << "." << implementation << ": Undefined implementation for block"; + LOG(ERROR) << role << " block: Undefined implementation " << implementation; } return block; } diff --git a/src/core/receiver/gnss_flowgraph.cc b/src/core/receiver/gnss_flowgraph.cc index 1566f3616..d37970fbd 100644 --- a/src/core/receiver/gnss_flowgraph.cc +++ b/src/core/receiver/gnss_flowgraph.cc @@ -128,7 +128,10 @@ void GNSSFlowgraph::init() // TODO: Create a class interface for SignalSources, derived from GNSSBlockInterface. // Include GetRFChannels in the interface to avoid read config parameters here // read the number of RF channels for each front-end - RF_Channels = configuration_->property(sig_source_.at(0)->role() + ".RF_channels", 0); + if (sig_source_.at(0) != nullptr) + { + RF_Channels = configuration_->property(sig_source_.at(0)->role() + ".RF_channels", 0); + } if (RF_Channels != 0) { for (int j = 0; j < RF_Channels; j++) @@ -265,6 +268,7 @@ void GNSSFlowgraph::start() catch (const std::exception& e) { LOG(ERROR) << "Unable to start flowgraph: " << e.what(); + print_help(); return; } @@ -321,11 +325,15 @@ void GNSSFlowgraph::connect() #if ENABLE_FPGA if (connect_fpga_flowgraph() != 0) { + LOG(ERROR) << "Unable to connect flowgraph with FPFA off-loading"; + print_help(); return; } #else if (connect_desktop_flowgraph() != 0) { + LOG(ERROR) << "Unable to connect flowgraph"; + print_help(); return; } #endif @@ -342,7 +350,7 @@ void GNSSFlowgraph::disconnect() if (!connected_) { - LOG(INFO) << "flowgraph was not connected"; + LOG(INFO) << "Flowgraph was not connected"; return; } connected_ = false; @@ -539,19 +547,28 @@ int GNSSFlowgraph::connect_fpga_flowgraph() DLOG(INFO) << "Blocks connected internally to the top_block"; // Connect the counter - if (configuration_->property(sig_source_.at(0)->role() + ".enable_FPGA", false) == false) + if (sig_source_.at(0) != nullptr) { - if (connect_sample_counter() != 0) + if (configuration_->property(sig_source_.at(0)->role() + ".enable_FPGA", false) == false) { - return 1; + if (connect_sample_counter() != 0) + { + return 1; + } + } + else + { + if (connect_fpga_sample_counter() != 0) + { + return 1; + } } } else { - if (connect_fpga_sample_counter() != 0) - { - return 1; - } + help_hint_ += " * Check implementation name for SignalSource block\n"; + help_hint_ += " Signal Source block implementation for FPGA off-loading should be 'Ad9361_Fpga_Signal_Source'\n"; + return 1; } if (connect_channels_to_observables() != 0) @@ -575,6 +592,9 @@ int GNSSFlowgraph::connect_fpga_flowgraph() { return 1; } + + check_desktop_conf_in_fpga_env(); + LOG(INFO) << "The GNU Radio flowgraph for the current GNSS-SDR configuration with FPGA off-loading has been successfully connected"; return 0; } @@ -654,13 +674,23 @@ int GNSSFlowgraph::connect_signal_sources() { for (int i = 0; i < sources_count_; i++) { - try + if (sig_source_.at(i) != nullptr) { - sig_source_.at(i)->connect(top_block_); + try + { + sig_source_.at(i)->connect(top_block_); + } + catch (const std::exception& e) + { + LOG(ERROR) << "Can't connect signal source block " << i << " internally: " << e.what(); + top_block_->disconnect_all(); + return 1; + } } - catch (const std::exception& e) + else { - LOG(ERROR) << "Can't connect signal source block " << i << " internally: " << e.what(); + help_hint_ += " * Check implementation name for SignalSource" + (i == 0 ? " " : (std::to_string(i) + " ")) + "block\n"; + help_hint_ += " Signal Source blocks documentation at https://gnss-sdr.org/docs/sp-blocks/signal-source/\n"; top_block_->disconnect_all(); return 1; } @@ -689,17 +719,95 @@ int GNSSFlowgraph::disconnect_signal_sources() } +int GNSSFlowgraph::connect_signal_conditioners() +{ + for (auto& sig : sig_conditioner_) + { + try + { + sig->connect(top_block_); + } + catch (const std::exception& e) + { + LOG(ERROR) << "Can't connect signal conditioner block internally: " << e.what(); + top_block_->disconnect_all(); + std::string reported_error(e.what()); + if (std::string::npos != reported_error.find(std::string("itemsize mismatch"))) + { + std::string replace_me("copy"); + size_t pos = reported_error.find(replace_me); + size_t len = replace_me.length(); + reported_error.replace(pos, len, "PassThrough"); + help_hint_ += " * Blocks within the Signal Conditioner are connected with mismatched item size\n"; + help_hint_ += " Reported error: " + reported_error + '\n'; + help_hint_ += " Check the Signal Conditioner documentation at https://gnss-sdr.org/docs/sp-blocks/signal-conditioner/\n"; + } + if (std::string::npos != reported_error.find(std::string("DataTypeAdapter"))) + { + help_hint_ += " * The DataTypeAdapter implementation set in the configuration file does not exist\n"; + help_hint_ += " Check the DataTypeAdapter documentation at https://gnss-sdr.org/docs/sp-blocks/data-type-adapter/\n"; + } + if (std::string::npos != reported_error.find(std::string("InputFilter"))) + { + help_hint_ += " * The InputFilter implementation set in the configuration file does not exist\n"; + help_hint_ += " Check the InputFilter documentation at https://gnss-sdr.org/docs/sp-blocks/input-filter/\n"; + } + if (std::string::npos != reported_error.find(std::string("Resampler"))) + { + help_hint_ += " * The Resampler implementation set in the configuration file does not exist\n"; + help_hint_ += " Check the Resampler documentation at https://gnss-sdr.org/docs/sp-blocks/resampler/\n"; + } + return 1; + } + } + DLOG(INFO) << "Signal Conditioner blocks successfully connected to the top_block"; + return 0; +} + + +int GNSSFlowgraph::disconnect_signal_conditioners() +{ + for (auto& sig : sig_conditioner_) + { + try + { + sig->disconnect(top_block_); + } + catch (const std::exception& e) + { + LOG(INFO) << "Can't disconnect signal conditioner block internally: " << e.what(); + top_block_->disconnect_all(); + return 1; + } + } + return 0; +} + + int GNSSFlowgraph::connect_channels() { for (int i = 0; i < channels_count_; i++) { - try + if (channels_.at(i) != nullptr) { - channels_.at(i)->connect(top_block_); + try + { + channels_.at(i)->connect(top_block_); + } + catch (const std::exception& e) + { + LOG(ERROR) << "Can't connect channel " << i << " internally: " << e.what(); + top_block_->disconnect_all(); + return 1; + } } - catch (const std::exception& e) + else { - LOG(ERROR) << "Can't connect channel " << i << " internally: " << e.what(); + LOG(ERROR) << "Can't connect channel " << i << " internally"; + help_hint_ += " * Check implementation names for Channel" + std::to_string(i) + " inner blocks.\n"; + help_hint_ += " Acquisition blocks documentation at https://gnss-sdr.org/docs/sp-blocks/acquisition/\n"; + help_hint_ += " Tracking blocks documentation at https://gnss-sdr.org/docs/sp-blocks/tracking/\n"; + help_hint_ += " Telemetry Decoder blocks documentation at https://gnss-sdr.org/docs/sp-blocks/telemetry-decoder/\n"; top_block_->disconnect_all(); return 1; } @@ -730,6 +838,13 @@ int GNSSFlowgraph::disconnect_channels() int GNSSFlowgraph::connect_observables() { + if (observables_ == nullptr) + { + help_hint_ += " * Check implementation name for the Observables block\n"; + help_hint_ += " Observables block documentation at https://gnss-sdr.org/docs/sp-blocks/observables/\n"; + top_block_->disconnect_all(); + return 1; + } try { observables_->connect(top_block_); @@ -763,6 +878,13 @@ int GNSSFlowgraph::disconnect_observables() int GNSSFlowgraph::connect_pvt() { + if (pvt_ == nullptr) + { + help_hint_ += " * Check implementation name for the PVT block\n"; + help_hint_ += " PVT block documentation at https://gnss-sdr.org/docs/sp-blocks/pvt/\n"; + top_block_->disconnect_all(); + return 1; + } try { pvt_->connect(top_block_); @@ -840,6 +962,7 @@ int GNSSFlowgraph::disconnect_sample_counter() return 0; } + #if ENABLE_FPGA int GNSSFlowgraph::connect_fpga_sample_counter() { @@ -918,6 +1041,8 @@ int GNSSFlowgraph::connect_signal_sources_to_signal_conditioners() // GNURADIO max_streams=-1 means infinite ports! DLOG(INFO) << "sig_source_.at(i)->get_right_block()->output_signature()->max_streams()=" << sig_source_.at(i)->get_right_block()->output_signature()->max_streams(); DLOG(INFO) << "sig_conditioner_.at(signal_conditioner_ID)->get_left_block()->input_signature()=" << sig_conditioner_.at(signal_conditioner_ID)->get_left_block()->input_signature()->max_streams(); + size_t output_size = sig_source_.at(i)->item_size(); + size_t input_size = sig_conditioner_.at(signal_conditioner_ID)->get_left_block()->input_signature()->sizeof_stream_item(0); if (sig_source_.at(i)->get_right_block()->output_signature()->max_streams() > 1 or sig_source_.at(i)->get_right_block()->output_signature()->max_streams() == -1) { @@ -943,12 +1068,28 @@ int GNSSFlowgraph::connect_signal_sources_to_signal_conditioners() } } signal_conditioner_ID++; + // Check configuration inconsistencies + if (output_size != input_size) + { + help_hint_ += " * The Signal Source implementation " + sig_source_.at(i)->implementation() + " has an output with an item size of " + std::to_string(output_size) + " bytes, but it is connected to the Signal Conditioner implementation " + sig_conditioner_.at(signal_conditioner_ID)->implementation() + " with input item size of " + std::to_string(input_size) + "bytes.\n"; + help_hint_ += " Output ports must be connected to input ports with the same item size.\n"; + } } } } catch (const std::exception& e) { - LOG(ERROR) << "Can't connect signal source " << i << " to signal conditioner " << i << ": " << e.what(); + LOG(ERROR) << "Can't connect SignalSource" << (i == 0 ? " " : (std::to_string(i) + " ")) << "to SignalConditioner" << (i == 0 ? " " : (std::to_string(i) + " ")) << ": " << e.what(); + std::string reported_error(e.what()); + if (std::string::npos != reported_error.find(std::string("itemsize mismatch"))) + { + std::string replace_me("copy"); + size_t pos = reported_error.find(replace_me); + size_t len = replace_me.length(); + reported_error.replace(pos, len, "PassThrough"); + help_hint_ += " * The SignalSource output item size and the SignalConditioner input item size are mismatched\n"; + help_hint_ += " Reported error: " + reported_error + '\n'; + } top_block_->disconnect_all(); return 1; } @@ -959,6 +1100,65 @@ int GNSSFlowgraph::connect_signal_sources_to_signal_conditioners() } +int GNSSFlowgraph::disconnect_signal_sources_from_signal_conditioners() +{ + int RF_Channels = 0; + int signal_conditioner_ID = 0; + for (int i = 0; i < sources_count_; i++) + { + try + { + // TODO: Remove this array implementation and create generic multistream connector + // (if a signal source has more than 1 stream, then connect it to the multistream signal conditioner) + if (sig_source_.at(i)->implementation() == "Raw_Array_Signal_Source") + { + // Multichannel Array + for (int j = 0; j < GNSS_SDR_ARRAY_SIGNAL_CONDITIONER_CHANNELS; j++) + { + top_block_->disconnect(sig_source_.at(i)->get_right_block(), j, sig_conditioner_.at(i)->get_left_block(), j); + } + } + else + { + // TODO: Create a class interface for SignalSources, derived from GNSSBlockInterface. + // Include GetRFChannels in the interface to avoid read config parameters here + // read the number of RF channels for each front-end + RF_Channels = configuration_->property(sig_source_.at(i)->role() + ".RF_channels", 1); + + for (int j = 0; j < RF_Channels; j++) + { + if (sig_source_.at(i)->get_right_block()->output_signature()->max_streams() > 1 or sig_source_.at(i)->get_right_block()->output_signature()->max_streams() == -1) + { + top_block_->disconnect(sig_source_.at(i)->get_right_block(), j, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); + } + else + { + if (j == 0) + { + // RF_channel 0 backward compatibility with single channel sources + top_block_->disconnect(sig_source_.at(i)->get_right_block(), 0, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); + } + else + { + // Multiple channel sources using multiple output blocks of single channel (requires RF_channel selector in call) + top_block_->disconnect(sig_source_.at(i)->get_right_block(j), 0, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); + } + } + signal_conditioner_ID++; + } + } + } + catch (const std::exception& e) + { + LOG(INFO) << "Can't disconnect signal source " << i << " to signal conditioner " << i << ": " << e.what(); + top_block_->disconnect_all(); + return 1; + } + } + return 0; +} + + int GNSSFlowgraph::connect_signal_conditioners_to_channels() { for (int i = 0; i < channels_count_; i++) @@ -1096,7 +1296,7 @@ int GNSSFlowgraph::connect_signal_conditioners_to_channels() } signal_conditioner_connected_.at(selected_signal_conditioner_ID) = true; // annotate that this signal conditioner is connected - DLOG(INFO) << "signal conditioner " << selected_signal_conditioner_ID << " successfully connected to channel " << i; + DLOG(INFO) << "Signal conditioner " << selected_signal_conditioner_ID << " successfully connected to channel " << i; } return 0; } @@ -1349,104 +1549,6 @@ int GNSSFlowgraph::disconnect_monitors() } -int GNSSFlowgraph::connect_signal_conditioners() -{ - for (auto& sig : sig_conditioner_) - { - try - { - sig->connect(top_block_); - } - catch (const std::exception& e) - { - LOG(ERROR) << "Can't connect signal conditioner block internally: " << e.what(); - top_block_->disconnect_all(); - return 1; - } - } - DLOG(INFO) << "Signal Conditioner blocks successfully connected to the top_block"; - return 0; -} - - -int GNSSFlowgraph::disconnect_signal_conditioners() -{ - for (auto& sig : sig_conditioner_) - { - try - { - sig->disconnect(top_block_); - } - catch (const std::exception& e) - { - LOG(INFO) << "Can't disconnect signal conditioner block internally: " << e.what(); - top_block_->disconnect_all(); - return 1; - } - } - return 0; -} - - -int GNSSFlowgraph::disconnect_signal_sources_from_signal_conditioners() -{ - int RF_Channels = 0; - int signal_conditioner_ID = 0; - for (int i = 0; i < sources_count_; i++) - { - try - { - // TODO: Remove this array implementation and create generic multistream connector - // (if a signal source has more than 1 stream, then connect it to the multistream signal conditioner) - if (sig_source_.at(i)->implementation() == "Raw_Array_Signal_Source") - { - // Multichannel Array - for (int j = 0; j < GNSS_SDR_ARRAY_SIGNAL_CONDITIONER_CHANNELS; j++) - { - top_block_->disconnect(sig_source_.at(i)->get_right_block(), j, sig_conditioner_.at(i)->get_left_block(), j); - } - } - else - { - // TODO: Create a class interface for SignalSources, derived from GNSSBlockInterface. - // Include GetRFChannels in the interface to avoid read config parameters here - // read the number of RF channels for each front-end - RF_Channels = configuration_->property(sig_source_.at(i)->role() + ".RF_channels", 1); - - for (int j = 0; j < RF_Channels; j++) - { - if (sig_source_.at(i)->get_right_block()->output_signature()->max_streams() > 1 or sig_source_.at(i)->get_right_block()->output_signature()->max_streams() == -1) - { - top_block_->disconnect(sig_source_.at(i)->get_right_block(), j, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); - } - else - { - if (j == 0) - { - // RF_channel 0 backward compatibility with single channel sources - top_block_->disconnect(sig_source_.at(i)->get_right_block(), 0, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); - } - else - { - // Multiple channel sources using multiple output blocks of single channel (requires RF_channel selector in call) - top_block_->disconnect(sig_source_.at(i)->get_right_block(j), 0, sig_conditioner_.at(signal_conditioner_ID)->get_left_block(), 0); - } - } - signal_conditioner_ID++; - } - } - } - catch (const std::exception& e) - { - LOG(INFO) << "Can't disconnect signal source " << i << " to signal conditioner " << i << ": " << e.what(); - top_block_->disconnect_all(); - return 1; - } - } - return 0; -} - - void GNSSFlowgraph::check_signal_conditioners() { // check for unconnected signal conditioners and connect null_sinks @@ -1597,6 +1699,35 @@ void GNSSFlowgraph::assign_channels() } +void GNSSFlowgraph::print_help() +{ + if (!help_hint_.empty()) + { + std::cerr << "It seems that your configuration file is not well defined.\n"; + std::cerr << "A hint to fix your configuration file:\n"; + std::cerr << help_hint_; + } +} + + +void GNSSFlowgraph::check_desktop_conf_in_fpga_env() +{ + int number_of_fpga_acq_channels = 0; + for (int i = 0; i < channels_count_; i++) + { + if (channels_.at(i)->get_left_block_acq() == nullptr) + { + number_of_fpga_acq_channels++; + } + } + if (number_of_fpga_acq_channels != channels_count_) + { + help_hint_ += " * The Acquisition block implementation is not suitable for GNSS-SDR flowgraph with FPGA off-loading\n"; + help_hint_ += " If you want to use this configuration in an environment without FPGA, please rebuild GNSS-SDR with CMake option '-DENABLE_FPGA=OFF'\n"; + } +} + + bool GNSSFlowgraph::send_telemetry_msg(const pmt::pmt_t& msg) { // Push ephemeris to PVT telemetry msg in port using a channel out port diff --git a/src/core/receiver/gnss_flowgraph.h b/src/core/receiver/gnss_flowgraph.h index 49a004589..8b71bb672 100644 --- a/src/core/receiver/gnss_flowgraph.h +++ b/src/core/receiver/gnss_flowgraph.h @@ -216,6 +216,8 @@ private: void push_back_signal(const Gnss_Signal& gs); void remove_signal(const Gnss_Signal& gs); + void print_help(); + void check_desktop_conf_in_fpga_env(); double project_doppler(const std::string& searched_signal, double primary_freq_doppler_hz); bool is_multiband() const; @@ -279,6 +281,7 @@ private: std::map mapStringValues_; std::string config_file_; + std::string help_hint_; std::mutex signal_list_mutex_;