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
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
// SPDX-FileCopyrightText: 2026 Martino Ferrari
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace udpscope {
|
||||
|
||||
/// A math channel's operations. `Source` reads another channel by name; `Sum`/`Sub`/`Mul`/`Div`
|
||||
/// combine two node values; the rest take one. `Sqrt` and `Clamp` are here because a scope's
|
||||
/// derived traces want them constantly (an envelope, a rectified-and-squared RMS input, a
|
||||
/// bounded control signal) and they cost the same to carry as the ones that were asked for.
|
||||
enum class MathOp {
|
||||
Source,
|
||||
Gain, ///< y = x * gain + offset
|
||||
Abs,
|
||||
Sqrt,
|
||||
Clamp, ///< y = min(max(x, lo), hi)
|
||||
LowPass, ///< biquad, corner `fc` Hz
|
||||
HighPass,
|
||||
BandPass, ///< `fc` Hz centre, `q` wide
|
||||
Sum, ///< a + b
|
||||
Sub, ///< a - b
|
||||
Mul, ///< a * b
|
||||
Div, ///< a / b, zero when b is 0 (a scope trace must not go to inf/NAN)
|
||||
};
|
||||
|
||||
const char* math_op_name(MathOp op);
|
||||
int math_op_count();
|
||||
MathOp math_op_at(int index);
|
||||
/// Inputs the op takes: 0 for `Source`, 1 for the unary ones, 2 for the arithmetic.
|
||||
int math_op_arity(MathOp op);
|
||||
/// Which of the per-node parameters an op actually reads, so a dialog can show only those rows --
|
||||
/// an option that does nothing for an op is left out rather than stored and ignored.
|
||||
bool math_op_uses_gain(MathOp op);
|
||||
bool math_op_uses_filter(MathOp op);
|
||||
bool math_op_uses_clamp(MathOp op);
|
||||
|
||||
/// One node of a math channel's graph. Node indices are positions in `MathChannel::nodes`, and a
|
||||
/// node may only read ones before it: the evaluator walks the list in order, which is what makes a
|
||||
/// cycle impossible by construction rather than by a check.
|
||||
struct MathNode {
|
||||
MathOp op = MathOp::Source;
|
||||
std::string source; ///< `Source`: the channel to read, by name.
|
||||
int in_a = -1; ///< other ops: node indices, -1 while unconnected.
|
||||
int in_b = -1;
|
||||
double gain = 1.0;
|
||||
double offset = 0.0;
|
||||
double fc = 100.0; ///< LP/HP corner, BP centre, Hz.
|
||||
double q = 0.707; ///< BP quality factor.
|
||||
double lo = -1.0; ///< `Clamp` bounds.
|
||||
double hi = 1.0;
|
||||
double x = 0.0; ///< the editor's box position, logical pixels.
|
||||
double y = 0.0;
|
||||
};
|
||||
|
||||
/// A derived channel: a graph of nodes and which node is the answer.
|
||||
struct MathChannel {
|
||||
std::string name; ///< user-facing; also how another graph names it as a `Source`.
|
||||
std::string unit; ///< optional. When empty, the first `Source` node's unit is used.
|
||||
bool enabled = false;
|
||||
std::vector<MathNode> nodes;
|
||||
int output = -1; ///< the node whose value is the channel's.
|
||||
};
|
||||
|
||||
/// The per-channel runtime: the filter memory and the per-node scratch. Kept apart from the
|
||||
/// `MathChannel` because it is *state* (one sample of history per biquad) while the channel is
|
||||
/// *configuration* -- so the pane can edit one while the producer streams through the other.
|
||||
struct MathRuntime {
|
||||
std::vector<double> value; ///< one per node, this sample.
|
||||
/// Biquad coefficients per node (transposed direct form II), and its two state words.
|
||||
std::vector<double> b0, b1, b2, a1, a2, z1, z2;
|
||||
/// The rate the coefficients were built for; a change rebuilds them.
|
||||
double dt = 0.0;
|
||||
bool ready = false;
|
||||
};
|
||||
|
||||
/// Fills `rt`'s size and the biquad coefficients for `dt` seconds per sample. Call it whenever the
|
||||
/// graph or the stored rate changes -- cheap, and it is what keeps the filters' cutoffs honest
|
||||
/// across a decimation change, where `dt` is not what it was when the user typed a frequency.
|
||||
void math_prepare(const MathChannel& channel, MathRuntime& rt, double dt);
|
||||
|
||||
/// One sample through the graph. `source(name)` returns that channel's current value, and NAN when
|
||||
/// it is not acquired -- which propagates to the output, so a math channel whose source is disabled
|
||||
/// reads as "no data" rather than as whatever the last good sample was.
|
||||
double math_step(const MathChannel& channel, MathRuntime& rt,
|
||||
const std::function<double(const std::string&)>& source);
|
||||
|
||||
/// Whether the graph is complete enough to produce a value: an output node that exists, and every
|
||||
/// input it needs connected to a node that exists (with `Source` nodes naming something).
|
||||
bool math_channel_valid(const MathChannel& channel, std::string* why = nullptr);
|
||||
|
||||
} // namespace udpscope
|
||||
Reference in new issue
Block a user