Correct downsampling and better layout
This commit is contained in:
1 parent
0d741c401b
commit
30db44eaa0
13 files changed
+1511
-252
No files matched your search
+33
-5
@@ -69,7 +69,12 @@ struct ChannelInfo {
|
||||
/// coincide and every slice is the same size.
|
||||
struct Partition {
|
||||
u64 time_capacity = 0; ///< time-base slots retained
|
||||
u64 time_stride = 1; ///< base ticks per time-base slot (reserved; 1)
|
||||
/// Base ticks per stored slot: the decimation factor the requested window forced. 1
|
||||
/// until the window needs more samples than the budget can hold. The ingest path
|
||||
/// *averages* each group of this many samples rather than keeping one of them (see
|
||||
/// `Acquisition::on_data()`), so this is a sample rate reduction with an anti-alias
|
||||
/// filter in front of it, not a stride.
|
||||
u64 time_stride = 1;
|
||||
std::vector<u64> stride; ///< time slots per channel sample (>= 1)
|
||||
std::vector<u64> value_capacity; ///< samples retained per channel
|
||||
double base_dt = 1.0; ///< seconds per base tick
|
||||
@@ -79,12 +84,23 @@ struct Partition {
|
||||
u64 total_bytes() const noexcept { return total_samples() * sizeof(f32); }
|
||||
/// Seconds between two time-base slots (the sample interval of every channel).
|
||||
double slot_dt() const noexcept { return base_dt * static_cast<double>(time_stride); }
|
||||
/// The rate samples are actually stored at, in samples per second.
|
||||
double stored_rate() const noexcept {
|
||||
const double dt = slot_dt();
|
||||
return dt > 0.0 ? 1.0 / dt : 0.0;
|
||||
}
|
||||
/// The anti-alias filter's corner, in Hz: half the stored rate, which is the new
|
||||
/// Nyquist. The boxcar the ingest path averages with has its first null at the stored
|
||||
/// rate itself, so everything that would fold back is attenuated.
|
||||
double antialias_hz() const noexcept { return 0.5 * stored_rate(); }
|
||||
};
|
||||
|
||||
/// Plans a partition for `channel_dt` cadences within `buffer_bytes`, targeting a
|
||||
/// span of `target_span_s` seconds. Throws std::runtime_error when the budget
|
||||
/// cannot hold even one sample per channel.
|
||||
Partition plan_partition(const std::vector<double>& channel_dt, double buffer_bytes);
|
||||
/// Plans a partition for `channel_dt` cadences within `buffer_bytes`, covering at least
|
||||
/// `target_span_s` seconds of stream -- decimating (with the ingest filter) by whatever
|
||||
/// power of two that takes. Throws std::runtime_error when the budget cannot hold even
|
||||
/// one sample per channel.
|
||||
Partition plan_partition(const std::vector<double>& channel_dt, double buffer_bytes,
|
||||
double target_span_s);
|
||||
|
||||
/// One of the two identical sample buffers: a shared time-base ring plus one
|
||||
/// value ring and one `ChannelBuffer` per channel.
|
||||
@@ -287,6 +303,14 @@ class ScopeModel {
|
||||
}
|
||||
double event_span() const noexcept { return event_span_.load(std::memory_order_relaxed); }
|
||||
|
||||
/// The window the buffer has to cover, which is the display window. Re-plans the
|
||||
/// partition -- and so drops the history -- only when the decimation factor it implies
|
||||
/// actually changes, so walking the window inside one decade costs nothing. Takes
|
||||
/// `mutex()` itself; do not call it holding the lock.
|
||||
/// Returns true when the buffer was rebuilt.
|
||||
bool set_target_span(double seconds);
|
||||
double target_span() const noexcept { return target_span_.load(std::memory_order_relaxed); }
|
||||
|
||||
/// Trigger state for the UI, published by the acquisition thread. Atomics
|
||||
/// rather than fields of `TriggerConfig` because the producer writes the arm
|
||||
/// state when a single-shot trigger disarms itself, and the frame tick reads it
|
||||
@@ -321,6 +345,10 @@ class ScopeModel {
|
||||
StreamStats stats_{};
|
||||
f64 epoch_ = 0.0;
|
||||
std::atomic<double> event_span_{1.0};
|
||||
/// The window the buffer is planned to cover. Read by `rebuild_locked()`, which a
|
||||
/// CONFIG can reach from the producer thread, so it is an atomic rather than a plain
|
||||
/// field.
|
||||
std::atomic<double> target_span_{0.0};
|
||||
std::atomic<bool> trigger_pending_{false};
|
||||
std::atomic<double> capture_edge_{0.0};
|
||||
std::atomic<std::shared_ptr<const EventSnapshot>> event_; ///< the event on screen
|
||||
|
||||
Reference in new issue
Block a user