From 092d2aa145137145187892e032973dc8e67fe190 Mon Sep 17 00:00:00 2001 From: Carles Fernandez Date: Thu, 17 Sep 2026 20:54:25 +0200 Subject: [PATCH] Fix unused private member, rename file_offset by seconds_to_skip, update comments --- .../gnss-sdr_EVK1029_Galileo_GPS_E1_E5a.conf | 2 +- .../adapters/evk1029_signal_source.cc | 43 ++++++------- .../adapters/evk1029_signal_source.h | 25 ++++---- .../gnuradio_blocks/evk1029_source.cc | 24 +++----- .../gnuradio_blocks/evk1029_source.h | 61 +++++++++---------- 5 files changed, 68 insertions(+), 87 deletions(-) diff --git a/conf/File_input/MultiCons/gnss-sdr_EVK1029_Galileo_GPS_E1_E5a.conf b/conf/File_input/MultiCons/gnss-sdr_EVK1029_Galileo_GPS_E1_E5a.conf index 9834bc719..f5218bd3f 100644 --- a/conf/File_input/MultiCons/gnss-sdr_EVK1029_Galileo_GPS_E1_E5a.conf +++ b/conf/File_input/MultiCons/gnss-sdr_EVK1029_Galileo_GPS_E1_E5a.conf @@ -63,7 +63,7 @@ SignalSource0.sampling_frequency = 177500000 SignalSource0.RF_channels = 2 ; one IF conditioner per band below ; Jump straight to a time of interest (seconds) in a long capture instead ; of always reprocessing from the start (see #1114): -;SignalSource0.file_offset = 3600 +;SignalSource0.seconds_to_skip = 3600 ;SignalSource0.dump = true ;SignalSource0.dump_filename = ./evk1029_signal_source.dat diff --git a/src/algorithms/signal_source/adapters/evk1029_signal_source.cc b/src/algorithms/signal_source/adapters/evk1029_signal_source.cc index 6072779c9..9d8e4c976 100644 --- a/src/algorithms/signal_source/adapters/evk1029_signal_source.cc +++ b/src/algorithms/signal_source/adapters/evk1029_signal_source.cc @@ -42,42 +42,35 @@ Evk1029SignalSource::Evk1029SignalSource( const std::string filename = configuration->property(role + ".filename"s, "data.bin"s); // NOTE: this must be the RAW (pre-decimation) ADC sample rate of the capture // file (e.g. the .sdrx metadata's ), NOT GNSS-SDR.internal_fs_sps - // (which is the decimated working rate downstream of InputFilter). It is - // only used to pace the throttle, when enabled. + // (which is the decimated working rate downstream of InputFilter). It paces + // the throttle (when enabled), sizes the block's scheduling quantum, and + // converts .seconds_to_skip into a position in the file. const int64_t default_fs = configuration->property("GNSS-SDR.internal_fs_sps"s, int64_t(0)); const int64_t fs = configuration->property(role + ".sampling_frequency"s, default_fs); - // Lets a long capture be jumped straight to a "time of interest" (e.g. - // to check effective signal strength at a specific point) instead of - // always reprocessed from the start. Expressed in seconds, not bytes -- - // fs (the raw capture's own sample rate) is already known right here, - // so converting is trivial and saves the caller from hand-computing a - // byte count themselves. Two OBA samples are packed per byte, hence the - // /2; Evk1029Source rounds the resulting byte count up to the next - // 64-bit word boundary itself (see its header) before seeking. - const double file_offset_s = configuration->property(role + ".file_offset"s, 0.0); - const auto file_offset_bytes = (file_offset_s > 0.0 && fs > 0) - ? static_cast(std::llround(file_offset_s * static_cast(fs) / 2.0)) - : uint64_t(0); + const double seconds_to_skip = configuration->property(role + ".seconds_to_skip"s, 0.0); + uint64_t bytes_to_skip = 0; + if (seconds_to_skip > 0.0 && fs > 0) + { + bytes_to_skip = static_cast(std::llround(seconds_to_skip * static_cast(fs) / 2.0)); + if (!configuration->is_present(role + ".sampling_frequency"s)) + { + LOG(WARNING) << role << ".seconds_to_skip is set but " << role << ".sampling_frequency is not: " + << "converting seconds into a file position with GNSS-SDR.internal_fs_sps=" << fs + << " Sps, which is wrong if the Input Filter decimates. Set " << role + << ".sampling_frequency to the raw ADC rate of the capture."; + } + } if (rf_channels_ == 0) { rf_channels_ = 1; } - if (enable_throttle_control_ && rf_channels_ > 1) - { - LOG(WARNING) << role << ".enable_throttle_control is not supported with RF_channels > 1 " - << "(would need its own N-independent-readers wiring, same as Evk1029Source itself; " - << "not yet implemented); ignoring it."; - std::cout << role << ": enable_throttle_control ignored with RF_channels=" << rf_channels_ << "\n"; - enable_throttle_control_ = false; - } - DLOG(INFO) << "EVK1029 Signal Source: filename=" << filename << ", fs=" << fs << ", item_size=" << item_size_ - << ", RF_channels=" << rf_channels_ << ", file_offset_s=" << file_offset_s; + << ", RF_channels=" << rf_channels_ << ", seconds_to_skip=" << seconds_to_skip; - evk1029_source_ = evk1029_make_source(filename, static_cast(rf_channels_), queue, static_cast(fs), file_offset_bytes); + evk1029_source_ = evk1029_make_source(filename, queue, static_cast(fs), bytes_to_skip); if (enable_throttle_control_) { diff --git a/src/algorithms/signal_source/adapters/evk1029_signal_source.h b/src/algorithms/signal_source/adapters/evk1029_signal_source.h index cf083d2eb..c46ce6f33 100644 --- a/src/algorithms/signal_source/adapters/evk1029_signal_source.h +++ b/src/algorithms/signal_source/adapters/evk1029_signal_source.h @@ -9,21 +9,18 @@ * for the current EVK1029 capture format (see evk1029_source.h). * * Set role + ".RF_channels" to N > 1 (e.g. for a multi-band setup sharing a - * single IF capture) to get N independent readers of ONE underlying + * single IF capture) to feed N Signal Conditioners from ONE underlying * Evk1029Source instance/read, instead of instantiating N separate - * SignalSourceN blocks each reading the file independently -- the latter - * has no shared read position, so a long, slow file open/seek on one of - * them can leave it at a different point in the capture than the others. - * Evk1029Source itself only ever declares a single real output port (see - * its own header); gnss_flowgraph.cc's generic signal-source-to-conditioner - * wiring detects this (output_signature()->max_streams() == 1) and connects - * each of the N signal conditioners to that same port independently, using - * GNU Radio's native support for multiple readers on one producer port. - * Each reader then advances at its own pace -- one RF band's downstream - * chain running behind never blocks the others. - * In RF_channels > 1 mode, .enable_throttle_control is not supported (a - * single throttle block would itself need N independent readers wired the - * same way, not yet implemented); it is ignored with a warning. + * SignalSourceN blocks each reading the file independently. Evk1029Source + * has a single output port (see its own header); gnss_flowgraph.cc's + * generic signal-source-to-conditioner wiring detects this + * (output_signature()->max_streams() == 1) and connects each of the N + * Signal Conditioners to that same port, so all of them read the same + * sample stream and GNU Radio keeps them within one output buffer of each + * other. The optional throttle sits between the source and that shared + * port, so .enable_throttle_control works for any RF_channels value. + * Set role + ".seconds_to_skip" to start reading at a given time into the + * capture (converted to a byte position using the raw sampling frequency). * * ----------------------------------------------------------------------------- * diff --git a/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.cc b/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.cc index 66f7c52e1..46928d35f 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.cc +++ b/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.cc @@ -61,25 +61,21 @@ int output_multiple_from_sampling_frequency(double sampling_frequency) } // namespace -Evk1029Source_sptr evk1029_make_source(const std::string& filename, int n_streams, Concurrent_Queue* queue, double sampling_frequency, uint64_t file_offset_bytes) +Evk1029Source_sptr evk1029_make_source(const std::string& filename, Concurrent_Queue* queue, double sampling_frequency, uint64_t bytes_to_skip) { - return Evk1029Source_sptr(new Evk1029Source(filename, n_streams, queue, sampling_frequency, file_offset_bytes)); + return Evk1029Source_sptr(new Evk1029Source(filename, queue, sampling_frequency, bytes_to_skip)); } -Evk1029Source::Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue* queue, double sampling_frequency, uint64_t file_offset_bytes) +Evk1029Source::Evk1029Source(const std::string& filename, Concurrent_Queue* queue, double sampling_frequency, uint64_t bytes_to_skip) : gr::sync_block("evk1029_source", gr::io_signature::make(0, 0, 0), gr::io_signature::make(1, 1, sizeof(int8_t))), queue_(queue), buffer_(kReadChunkBytes), buffer_valid_(0), - buffer_pos_(0), - n_streams_(n_streams) + buffer_pos_(0) { - // Exactly one real output port, always -- see this class's header for - // why. n_streams_ (the RF_channels count) is kept only for logging; - // it no longer sizes the io_signature. // Keep the scheduler from calling work() with a tiny noutput_items; each // unit of work produces a pair of samples (one input byte -> 2 samples). // This block runs at the raw, pre-decimation sample rate (up to ~180 Msps), @@ -100,26 +96,26 @@ Evk1029Source::Evk1029Source(const std::string& filename, int n_streams, Concurr exit(1); } - if (file_offset_bytes > 0) + if (bytes_to_skip > 0) { // Round up to the next 64-bit (8-byte) word boundary -- see this // parameter's doc comment in the header for why. constexpr uint64_t kWordBytes = 8; - const uint64_t aligned_offset = ((file_offset_bytes + kWordBytes - 1) / kWordBytes) * kWordBytes; + const uint64_t aligned_offset = ((bytes_to_skip + kWordBytes - 1) / kWordBytes) * kWordBytes; binary_input_file_.seekg(0, std::ios::end); const auto file_size = static_cast(binary_input_file_.tellg()); if (aligned_offset >= file_size) { - std::cerr << "EVK1029_Source: file_offset (" << aligned_offset - << " bytes, aligned) is at or beyond the end of '" << filename + std::cerr << "EVK1029_Source: seconds_to_skip lands at byte " << aligned_offset + << " (rounded up to a 64-bit word), at or beyond the end of '" << filename << "' (" << file_size << " bytes) -- nothing to read.\n"; exit(1); } binary_input_file_.seekg(static_cast(aligned_offset), std::ios::beg); - std::cout << "EVK1029_Source: skipping to file offset " << aligned_offset - << " bytes (requested " << file_offset_bytes << ", rounded up to a 64-bit word boundary).\n"; + std::cout << "EVK1029_Source: skipping the first " << aligned_offset << " bytes of the capture (requested " + << bytes_to_skip << ", rounded up to a 64-bit word boundary)" << std::endl; } } diff --git a/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.h b/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.h index 3782a38f4..e3259737f 100644 --- a/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.h +++ b/src/algorithms/signal_source/gnuradio_blocks/evk1029_source.h @@ -11,23 +11,20 @@ * 4-bit OBA samples per byte (16 samples per 64-bit word, per the * capture's .sdrx metadata: quantization=4, packedbits=64, encoding=OBA). * - * Exactly ONE real output port, always (n_streams_ is kept only for - * logging -- it no longer sizes the io_signature). Multiple RF bands - * sharing this same raw capture (e.g. several Freq_Xlating_Fir_Filter - * instances tuned to different IFs) each get their OWN connect() to this - * one port instead (see gnss_flowgraph.cc's signal-source-to-conditioner - * wiring, which already takes this path whenever output_signature()'s - * max_streams() == 1): GNU Radio's buffer model natively supports several - * independent readers on one producer port, each with its own read - * pointer, so a downstream chain that temporarily falls behind only - * delays *itself* -- it can't force every port's own production to stall - * together the way N separate, GNU-Radio-scheduler-coupled sync_block - * output ports would (a single slow port failing GNU Radio's "space - * available on every port" check before work() is even called stalls - * production on every port at once). An earlier version of this block - * used N separate ports and copied the same decoded output into each of - * them; besides the tighter coupling above, that meant N-1 extra full- - * buffer memcpy calls per work() call at up to the raw capture rate. + * The block has a single output port. Multiple RF bands sharing the same + * raw capture (e.g. several Freq_Xlating_Fir_Filter instances tuned to + * different IFs) are each connected to that one port (see the + * signal-source-to-conditioner wiring in gnss_flowgraph.cc, which takes + * this path whenever output_signature()->max_streams() == 1): GNU Radio + * supports several readers on one producer port, each with its own read + * pointer over the same buffer. The scheduler still throttles this block + * to the slowest of those readers (buffer::space_available() is bounded by + * the reader with the most unconsumed items), exactly as it would with one + * buffer per port, so the bands cannot drift apart by more than one output + * buffer. What the single port saves is the N-1 extra output buffers and + * the N-1 full-buffer memcpy calls per work() call, at up to the raw + * capture rate, that a port-per-band design needs in order to duplicate + * the same decoded samples. * * ----------------------------------------------------------------------------- * @@ -62,19 +59,18 @@ class Evk1029Source; using Evk1029Source_sptr = gnss_shared_ptr; -// file_offset_bytes: byte position in the raw capture to seek to before the -// first read, letting a long capture be jumped straight to a "time of -// interest" instead of always reprocessed from the start (see -// Evk1029SignalSource.file_offset in the adapter, which converts a -// requested offset in seconds -- since the sampling frequency is already -// known there -- into this byte count). Rounded up to the next multiple of -// 8 bytes (64 bits) here, matching the capture's native packing (16 OBA -// samples per little-endian 64-bit word): this block itself only ever reads -// byte-by-byte and would decode correctly from any byte offset, but seeking -// to a non-word-aligned byte splits a 64-bit DMA word the real hardware -// always transferred as one unit, which needlessly complicates comparing a -// jumped-to run against a from-the-start one at the file/word level. -Evk1029Source_sptr evk1029_make_source(const std::string& filename, int n_streams, Concurrent_Queue* queue, double sampling_frequency, uint64_t file_offset_bytes = 0); +// bytes_to_skip: byte position in the raw capture at which reading starts, +// so that a long capture can be jumped straight to a time of interest +// instead of being processed from the beginning (the adapter derives it +// from its .seconds_to_skip parameter and the raw sampling frequency). +// Rounded up to the next multiple of 8 bytes (64 bits) here, matching the +// capture's native packing (16 OBA samples per little-endian 64-bit word): +// this block itself only ever reads byte-by-byte and would decode correctly +// from any byte offset, but seeking to a non-word-aligned byte splits a +// 64-bit DMA word the real hardware always transferred as one unit, which +// needlessly complicates comparing a jumped-to run against a from-the-start +// one at the file/word level. +Evk1029Source_sptr evk1029_make_source(const std::string& filename, Concurrent_Queue* queue, double sampling_frequency, uint64_t bytes_to_skip = 0); /*! * \brief Reads a continuous, header-less EVK1029 raw capture file and @@ -86,8 +82,8 @@ public: ~Evk1029Source() override; private: - friend Evk1029Source_sptr evk1029_make_source(const std::string& filename, int n_streams, Concurrent_Queue* queue, double sampling_frequency, uint64_t file_offset_bytes); - Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue* queue, double sampling_frequency, uint64_t file_offset_bytes = 0); + friend Evk1029Source_sptr evk1029_make_source(const std::string& filename, Concurrent_Queue* queue, double sampling_frequency, uint64_t bytes_to_skip); + Evk1029Source(const std::string& filename, Concurrent_Queue* queue, double sampling_frequency, uint64_t bytes_to_skip = 0); int work(int noutput_items, gr_vector_const_void_star& input_items, @@ -98,7 +94,6 @@ private: std::vector buffer_; std::size_t buffer_valid_; // number of valid bytes currently in buffer_ std::size_t buffer_pos_; // next unread byte offset within buffer_ - int n_streams_; };