fixed toolbar order and included math/measuraments files
This commit is contained in:
1 parent
aa2508e431
commit
714d042809
4 files changed
+677
No files matched your search
+121
@@ -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
|
||||
Reference in new issue
Block a user