feat: relative mode keeps the stream's absolute clock for the export

This commit is contained in:
Martino Ferrari committed 2026-10-08 17:20:47 +02:00
1 parent d11e7d48c4
commit 57e2f6aa8b
6 files changed
+87 -9

No files matched your search

+24 -5
View File
@@ -43,7 +43,7 @@ constexpr std::uint64_t kMathCostSample = 1024;
} // namespace
Partition plan_partition(const std::vector<double>& channel_dt, double buffer_bytes,
double target_span_s) {
double target_span_s, bool with_abs_time) {
Partition p;
const std::size_t n = channel_dt.size();
p.stride.assign(n, 1);
@@ -69,12 +69,14 @@ Partition plan_partition(const std::vector<double>& channel_dt, double buffer_by
// affects every channel equally and goes through the ingest filter.
for (std::size_t i = 0; i < n; ++i) p.stride[i] = 1;
p.abs_time = with_abs_time;
const u64 budget_bytes = static_cast<u64>(std::max(1.0, buffer_bytes));
// Weighted in *bytes*, not in slots: the shared clock is f64 and the values are f32
// (see `BufferStore::time()`), so a time slot costs twice a value sample and the two
// slices are not interchangeable. Counting both as one f32 is what would make
// `--buffer-mb` quietly understate the time ring by its own size.
double weight = static_cast<double>(sizeof(f64)); // the time slice itself
double weight = static_cast<double>(sizeof(f64)) * (with_abs_time ? 2.0 : 1.0); // the time slice(s)
for (u64 s : p.stride) weight += static_cast<double>(sizeof(f32)) / static_cast<double>(s);
const u64 slots = static_cast<u64>(static_cast<double>(budget_bytes) / weight);
@@ -99,7 +101,7 @@ Partition plan_partition(const std::vector<double>& channel_dt, double buffer_by
return std::max<u64>(pow2_floor(ticks / stride), 1);
};
const auto bytes_for = [&](u64 ticks) {
u64 total = ticks * sizeof(f64);
u64 total = ticks * sizeof(f64) * (with_abs_time ? 2u : 1u);
for (u64 s : p.stride) total += capacity_for(ticks, s) * sizeof(f32);
return total;
};
@@ -139,6 +141,11 @@ BufferStore::BufferStore(const Partition& part, f64 span_s)
: part_(part),
time_(std::make_shared<plot::data::RingBuffer<f64>>(part.time_capacity)) {
(void)span_s;
// Same capacity as the clock it parallels: the two are written on the same slots, so a
// smaller ring would make an export's stamps run out before its values do.
if (part.abs_time) {
abs_time_ = std::make_shared<plot::data::RingBuffer<f64>>(part.time_capacity);
}
const std::size_t n = part.value_capacity.size();
values_.reserve(n);
channels_.reserve(n);
@@ -340,7 +347,7 @@ void ScopeModel::rebuild_locked() {
}
part_ = plan_partition(dts, cfg_.buffer_mb * 1024.0 * 1024.0,
target_span_.load(std::memory_order_relaxed));
target_span_.load(std::memory_order_relaxed), cfg_.relative_time);
store_ = std::make_unique<BufferStore>(part_, part_.span_s);
if (trigger_.channel >= static_cast<int>(channels_.size())) trigger_.channel = -1;
@@ -358,7 +365,7 @@ bool ScopeModel::set_target_span(double seconds) {
std::vector<double> dts;
dts.reserve(channels_.size());
for (const ChannelInfo& c : channels_) dts.push_back(c.dt);
const Partition want = plan_partition(dts, cfg_.buffer_mb * 1024.0 * 1024.0, seconds);
const Partition want = plan_partition(dts, cfg_.buffer_mb * 1024.0 * 1024.0, seconds, cfg_.relative_time);
if (want.time_stride == part_.time_stride) return false;
std::fprintf(stderr,
"[udpscope] window %.4f s -> %.4f s: decimation %llu -> %llu, stored rate "
@@ -524,6 +531,12 @@ void ScopeModel::publish_pause_view(double lo_s, double hi_s, double x_offset) {
src.time().copy_from(first, n, snap->stamps.data());
for (f64& t : snap->stamps) t -= x_offset;
}
if (const plot::data::RingBuffer<f64>* abs = src.abs_time(); abs != nullptr) {
snap->abs_stamps.resize(static_cast<std::size_t>(n));
abs->copy_from(first, n, snap->abs_stamps.data());
// Absolute seconds outright -- deliberately not rebased like `stamps`.
for (f64& t : snap->abs_stamps) t += epoch_;
}
snap->values.resize(channels_.size());
snap->y.resize(channels_.size(), plot::Range{0.0, 0.0});
snap->pyramids.resize(channels_.size());
@@ -588,6 +601,12 @@ void ScopeModel::publish_event(double pre_s, double post_s, double edge) {
// disagree with.
for (auto& t : snap->stamps) t -= edge;
}
if (const plot::data::RingBuffer<f64>* abs = src.abs_time(); abs != nullptr) {
snap->abs_stamps.resize(static_cast<std::size_t>(n));
abs->copy_from(first, n, snap->abs_stamps.data());
// Absolute seconds outright -- deliberately not rebased like `stamps`.
for (f64& t : snap->abs_stamps) t += epoch_;
}
snap->values.resize(channels_.size());
snap->y.resize(channels_.size(), plot::Range{0.0, 0.0});