Add EVK1029 file_offset config to seek into a raw capture

Evk1029SignalSource.file_offset (seconds, default 0 = disabled) lets a
long capture be jumped straight to a time of interest instead of always
reprocessed from the start. Converted to a byte count using the source's
own sampling frequency, then rounded up to the next 64-bit word boundary
in Evk1029Source before seeking, matching the capture's native packing
(16 OBA samples per little-endian 64-bit word).
This commit is contained in:
joebre
2026-09-17 12:02:31 +02:00
parent 96b4921f8c
commit a6b63c77dc
3 changed files with 57 additions and 8 deletions
@@ -15,6 +15,7 @@
#include "evk1029_signal_source.h"
#include "configuration_interface.h"
#include <cmath>
#include <utility>
#if USE_GLOG_AND_GFLAGS
@@ -46,6 +47,19 @@ Evk1029SignalSource::Evk1029SignalSource(
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);
if (rf_channels_ == 0)
{
rf_channels_ = 1;
@@ -60,9 +74,10 @@ Evk1029SignalSource::Evk1029SignalSource(
enable_throttle_control_ = false;
}
DLOG(INFO) << "EVK1029 Signal Source: filename=" << filename << ", fs=" << fs << ", item_size=" << item_size_ << ", RF_channels=" << rf_channels_;
DLOG(INFO) << "EVK1029 Signal Source: filename=" << filename << ", fs=" << fs << ", item_size=" << item_size_
<< ", RF_channels=" << rf_channels_ << ", file_offset_s=" << file_offset_s;
evk1029_source_ = evk1029_make_source(filename, static_cast<int>(rf_channels_), queue, static_cast<double>(fs));
evk1029_source_ = evk1029_make_source(filename, static_cast<int>(rf_channels_), queue, static_cast<double>(fs), file_offset_bytes);
if (enable_throttle_control_)
{
@@ -62,13 +62,13 @@ 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)
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)
{
return Evk1029Source_sptr(new Evk1029Source(filename, n_streams, queue, sampling_frequency));
return Evk1029Source_sptr(new Evk1029Source(filename, n_streams, queue, sampling_frequency, file_offset_bytes));
}
Evk1029Source::Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency)
Evk1029Source::Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency, uint64_t file_offset_bytes)
: gr::sync_block("evk1029_source",
gr::io_signature::make(0, 0, 0),
gr::io_signature::make(n_streams, n_streams, sizeof(int8_t))),
@@ -97,6 +97,28 @@ Evk1029Source::Evk1029Source(const std::string& filename, int n_streams, Concurr
std::cerr << "EVK1029_Source: failed to open file: " << filename << '\n';
exit(1);
}
if (file_offset_bytes > 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;
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
<< "' (" << 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";
}
}
@@ -52,7 +52,19 @@ class Evk1029Source;
using Evk1029Source_sptr = gnss_shared_ptr<Evk1029Source>;
Evk1029Source_sptr evk1029_make_source(const std::string& filename, int n_streams, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency);
// 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);
/*!
* \brief Reads a continuous, header-less EVK1029 raw capture file and
@@ -65,8 +77,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);
Evk1029Source(const std::string& filename, int n_streams, Concurrent_Queue<pmt::pmt_t>* queue, double sampling_frequency);
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);
int work(int noutput_items,
gr_vector_const_void_star& input_items,