fixed toolbar order and included math/measuraments files

This commit is contained in:
Martino Ferrari committed 2026-10-03 17:39:29 +02:00
1 parent aa2508e431
commit 714d042809
4 files changed
+677

No files matched your search

+121
View File
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: MIT
// SPDX-FileCopyrightText: 2026 Martino Ferrari
#pragma once
#include <string>
#include <vector>
namespace udpscope {
/// What a measurement computes over the visible window of one signal.
enum class MeasureType {
Average, ///< the mean of every visible sample
Min, ///< the smallest visible value
Max, ///< the largest visible value
/// The largest swing in the window: `max - min`, the two extremes the eye would measure
/// with a ruler across the waveform.
PeakToPeak,
/// Half the peak-to-peak swing, `(max - min) / 2` -- the amplitude of a symmetric waveform
/// about its own mean, which is the figure a datasheet quotes and a signal generator is set
/// to. Deliberately not `max(|value|)`: that is a *distance from zero*, which for a signal
/// riding on an offset is a different and less useful number.
Amplitude,
Top, ///< the signal's high level (the mean of the samples above the threshold)
Bottom, ///< the signal's low level (the mean of the samples below it)
PulseDuration, ///< how long the signal spends on one side of the threshold
Period, ///< the time between two successive crossings of the threshold
Frequency, ///< 1 / period
};
/// Type-specific behaviour. Every field is meaningful only for the types named below; the
/// others ignore it (an option that does nothing for a type is left out of the dialog
/// rather than stored and ignored).
struct MeasureOptions {
/// `Top`/`Bottom`/`PulseDuration`/`Period`/`Frequency`: where the split is, as a share of
/// the window's own min..max. The middle by default, which is what a symmetric waveform
/// wants; a 3.3 V logic signal with ringing wants it nearer the top.
double threshold_pct = 50.0;
/// `PulseDuration`: which side of the threshold is "the pulse". `Period`/`Frequency`
/// use it as the edge direction, so one cycle is counted once.
bool above = true;
/// Draw the value on the plot: a ruler at a level (top/bottom), both rulers at the
/// extremes (min/max), the pulse's two edges as vertical markers (pulse duration). The
/// markers are readouts -- they follow the window, and the user cannot drag them.
bool show_on_plot = false;
};
/// One entry in the measurements pane, and everything needed to recompute it: the signal by
/// *name* (a CONFIG renumbers channels, so an index would start measuring another signal)
/// and the type with its options.
struct Measure {
std::string signal;
MeasureType type = MeasureType::Average;
MeasureOptions options;
};
/// One thing to draw on the plot for a measurement: a *level* (a horizontal ruler) or a
/// *time* (a vertical marker). A result carries up to two, and they need not be the same kind
/// -- `min` is a ruler at the value and a marker where the value occurred.
struct MeasureMarkSpec {
bool level = false;
double value = 0.0;
};
/// What a measurement computed. `valid` is what the pane shows as `--` when a type has
/// nothing to measure in the window (a period on a flat signal, a pulse that never crosses).
struct MeasureResult {
double primary = 0.0; ///< the value the pane prints
double secondary = 0.0; ///< pulse duration also reports the duty; unused otherwise
bool has_secondary = false;
std::vector<MeasureMarkSpec> marks; ///< what to draw, empty when the type draws nothing
bool valid = false;
};
/// The window's own extremes, which every threshold-relative type scales from and which
/// several types report directly. Public so a caller holding *several* measurements on one
/// signal can pay for the scan once: it is O(visible samples) and a live view at 1 MS/s makes
/// that the difference between a frame budget and a stall.
struct MeasureStats {
double lo = 0.0;
double hi = 0.0;
/// The sum and count of the finite samples: the mean, and the mean over a level, are both
/// derived from them, so the window is read once for every type that is not an edge count.
double sum = 0.0;
std::size_t count = 0;
/// The time of the *first* sample to reach each extreme -- "min" is a level and a place, and
/// finding the place is why this is not just the two numbers.
double at_lo = 0.0;
double at_hi = 0.0;
bool valid = false;
};
/// One pass over a window: both extremes with their first positions, and the sum and count.
MeasureStats measure_stats(const std::vector<double>& time, const std::vector<double>& value);
/// The engine: the visible samples of one signal, in stream seconds and in the signal's own
/// units. Both arrays are the same length and in time order; the function reads nothing else,
/// which is what makes it testable and what keeps the answer independent of the plot.
///
/// `stats` is `measure_stats(time, value)` when the caller already has it -- several measurements
/// on one signal share one scan -- and null to compute it here. It must describe these arrays.
MeasureResult compute_measure(const std::vector<double>& time, const std::vector<double>& value,
MeasureType type, const MeasureOptions& options,
const MeasureStats* stats = nullptr);
/// A measurement type's name, as the pane's row and the dialog's dropdown spell it.
const char* measure_type_name(MeasureType type);
/// The number of entries the dialog's dropdown holds; the index is `MeasureType`.
int measure_type_count();
MeasureType measure_type_at(int index);
/// Whether a type uses the threshold (see `MeasureOptions`), so the dialog can show the
/// field only then. Deliberately not the case for `Period`/`Frequency`: a period is the
/// spacing of the crossings of the *mid* level, one rising edge per cycle, and offering a
/// threshold there only invites a setting that makes the count wrong.
bool measure_uses_threshold(MeasureType type);
/// Whether a type uses the `above` side (only `PulseDuration`, where it says which half of
/// the waveform is the pulse).
bool measure_uses_side(MeasureType type);
/// Whether a type draws its result on the plot when asked.
bool measure_can_show_on_plot(MeasureType type);
} // namespace udpscope