Fix unused private member, rename file_offset by seconds_to_skip, update comments

This commit is contained in:
Carles Fernandez
2026-09-18 10:02:24 +02:00
committed by Carles Fernandez
parent 4d34b79f89
commit 092d2aa145
5 changed files with 68 additions and 87 deletions
@@ -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
@@ -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 <freqbase>), 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<uint64_t>(std::llround(file_offset_s * static_cast<double>(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<uint64_t>(std::llround(seconds_to_skip * static_cast<double>(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<int>(rf_channels_), queue, static_cast<double>(fs), file_offset_bytes);
evk1029_source_ = evk1029_make_source(filename, queue, static_cast<double>(fs), bytes_to_skip);
if (enable_throttle_control_)
{
@@ -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).
*
* -----------------------------------------------------------------------------
*
@@ -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<pmt::pmt_t>* queue, double sampling_frequency, uint64_t file_offset_bytes)
Evk1029Source_sptr evk1029_make_source(const std::string& filename, Concurrent_Queue<pmt::pmt_t>* 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<pmt::pmt_t>* queue, double sampling_frequency, uint64_t file_offset_bytes)
Evk1029Source::Evk1029Source(const std::string& filename, Concurrent_Queue<pmt::pmt_t>* 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<uint64_t>(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<std::streamoff>(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;
}
}
@@ -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<Evk1029Source>;
// 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<pmt::pmt_t>* 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<pmt::pmt_t>* 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<pmt::pmt_t>* queue, double sampling_frequency, uint64_t file_offset_bytes);
Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency, uint64_t file_offset_bytes = 0);
friend Evk1029Source_sptr evk1029_make_source(const std::string& filename, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency, uint64_t bytes_to_skip);
Evk1029Source(const std::string& filename, Concurrent_Queue<pmt::pmt_t>* 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<uint8_t> 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_;
};