added math signals

This commit is contained in:
Martino Ferrari committed 2026-10-03 17:28:17 +02:00
1 parent 02620ca946
commit aa2508e431
10 files changed
+1773 -27

No files matched your search

+44 -1
View File
@@ -16,6 +16,8 @@
#include <memory>
#include <mutex>
#include <string>
#include "math_channel.hpp"
#include <vector>
namespace udpscope {
@@ -23,6 +25,10 @@ namespace udpscope {
/// One streamed signal, as the CONFIG packet described it.
struct SignalInfo {
std::string name;
/// True for the entries the model *synthesised* for a math channel: the stream never sends them,
/// the producer fills their samples by evaluating the graph. The flag is what lets a CONFIG
/// update tell them from the stream's own signals and put them back afterwards.
bool math = false;
std::string unit;
std::uint8_t type_code = 0;
std::uint8_t quant_type = 0;
@@ -50,7 +56,13 @@ struct ChannelInfo {
double offset = 0.0;
std::string name; ///< the signal's name
std::string unit;
/// True for a channel the producer *derives* rather than reads. The ingest must skip it: it has
/// no packet signal, so reading one gives zero -- and pushing that zero into a ring whose time
/// ring advances once per slot is what made a math channel alternate between its value and 0, and
/// then drift off the time ring entirely once the buffer was full.
bool math = false;
std::string unit; ///< the signal's unit, or a math channel's own
/// Samples of this channel per DATA packet (`elements()` of the signal). A
/// UDPStreamer signal is one channel: its columns are consecutive samples of the
@@ -178,6 +190,20 @@ class ScopeModel {
/// Takes a new CONFIG: rebuilds the signal and channel lists and the two
/// buffers. Clears the trigger's channel selection if the stream changed.
void set_signals(std::vector<SignalInfo> signals);
/// The user's math channels, as configured. Read it under the lock, like the other accessors
/// here -- the UI copies what it needs while it holds it.
std::vector<MathChannel> math_channels() const { return math_; }
/// Replaces the math channels. A *structural* change, exactly like a CONFIG or an
/// enable/disable: the enabled ones are re-appended to the signal list, their channels are
/// rebuilt and their runtimes re-prepared for the new stored rate. **Takes the lock itself** --
/// do not call it holding `mutex()`, which is not recursive.
void set_math_channels(std::vector<MathChannel> channels);
/// One stored sample's worth of derived values, called by the producer right after it has pushed
/// the streamed channels. `sample[c]` is channel `c`'s value for this slot; the math channels are
/// evaluated from those, in order, so a math channel may use another one.
void push_math_sample(const std::vector<f32>& sample);
const std::vector<SignalInfo>& signals() const noexcept { return signals_; }
const std::vector<ChannelInfo>& channels() const noexcept { return channels_; }
@@ -387,10 +413,27 @@ class ScopeModel {
private:
void rebuild_channels();
/// Re-appends one synthetic signal per math channel, after the stream's own. Called under the
/// lock by both `set_signals()` (a CONFIG) and `set_math_channels()` (an edit).
void append_math_signals_locked();
/// Rebuilds the math runtimes for the *current* plan: the biquad coefficients follow the stored
/// sample interval, and their state is reset with them. Called at the end of `rebuild_locked()`,
/// so a window change that moves the decimation re-prepares the filters exactly as an edit does.
void prepare_math_locked();
Config cfg_;
std::vector<SignalInfo> signals_;
std::vector<ChannelInfo> channels_;
// --- math channels --------------------------------------------------------
std::vector<MathChannel> math_;
std::vector<MathRuntime> math_rt_;
/// The channel index each math channel was given by `rebuild_channels()`, -1 while it has none
/// (disabled, or before the first rebuild).
std::vector<int> math_channel_;
/// The names of the synthetic signal entries, so they can be lifted out of `signals_`/`enabled_`
/// before the stream's own are re-appended -- a CONFIG must not leave a stale derived channel
/// behind, and must not drop a live one either.
std::vector<std::string> math_signal_names_;
std::vector<bool> enabled_;
Partition part_;