Components

Components are C++ classes that use store objects for input/output/parameters/control. This let’s you easily tune and debug an application.

The instances of these classes are tuned at compile-time to reflect the objects in the store. Especially, no resources are used for (optional) fields that do not exist in the store, and all store lookups in the directory are done at compile-time. You need C++14 (or later), though.

Check out the components and control examples.

stored::Amplifier

template<typename Container, unsigned long long flags = 0, typename T = float>
class Amplifier

An offset/gain amplifier, based on store variables.

This class comes in very handy when converting ADC inputs to some SI-value. It includes an override field to force inputs to some test value.

To use this class, add a scope to your store, like:

{
    float input
    bool=true enable
    float=1 gain
    float=0 offset
    float=-inf low
    float=inf high
    float=nan override
    float output
} amp

All fields are optional. All variables of type float can be any other type, as long as it matches the template parameter T.

When not all fields are in the store, names may become ambiguous. For example, if override and output are not there, the store’s directory may resolve o to any of the three fields. In this case, you have to specify which fields are to be processed. For this, use the following ids:

field

id

input

I

enable

e

gain

g

offset

o

low

l

high

h

override

F

output

O

The amplifier basically does:

if(override is nan)
    output = min(high, max(low, input * gain + offset))
else
    output = override

Then, instantiate the amplifier like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto amp_o = stored::Amplifier<stored::YourStore>::objects("/amp/");

// Instantiate an Amplifier, tailored to the available fields in the store.
stored::Amplifier<stored::YourStore, amp_o.flags()> amp{amp_o, yourStore};

Or, for example when you know there are only the offset and gain fields in the store, and ambiguity must be resolved:

// Construct a compile-time object, which resolves only two fields in your store.
constexpr auto amp_o = stored::Amplifier<stored::YourStore>::objects<'o','g'>("/amp/");
stored::Amplifier<stored::YourStore, amp_o.flags()> amp{amp_o, yourStore};

Calling amp() now uses the input and produces the value in output. Alternatively, or when the input field is absent in the store, call amp(x), where x is the input.

Public Types

using Bound = typename AmplifierObjects<Container, type>::template Bound<flags>
using type = T

Public Functions

constexpr Amplifier() noexcept = default

Default ctor.

Use this when initialization is postponed. Do not access or run the Amplifier instance, as it does not hold proper references to a store. You can just assign another Amplifier instance.

inline constexpr Amplifier(AmplifierObjects<Container, type> const &o, Container &container)

Initialize the Amplifier, given a list of objects and a container.

inline void disable() noexcept

Disable the Amplifier.

Ignored when the enable object is not available.

inline void enable(bool value = true) noexcept

Enable (or disable) the Amplifier.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, which is true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type gain() const noexcept

Return the gain value, or 1 when not available.

inline decltype(auto) gainObject() const noexcept

Return the gain object.

inline decltype(auto) gainObject() noexcept

Return the gain object.

inline type high() const noexcept

Return the high value, or inf when not available.

inline decltype(auto) highObject() const noexcept

Return the high object.

inline decltype(auto) highObject() noexcept

Return the high object.

inline type input() const noexcept

Return the input value, or 0 when not available.

inline decltype(auto) inputObject() const noexcept

Return the input object.

inline decltype(auto) inputObject() noexcept

Return the input object.

inline type low() const noexcept

Return the low value, or -inf when not available.

inline decltype(auto) lowObject() const noexcept

Return the low object.

inline decltype(auto) lowObject() noexcept

Return the low object.

inline type offset() const noexcept

Return the offset value, or 1 when not available.

inline decltype(auto) offsetObject() const noexcept

Return the offset object.

inline decltype(auto) offsetObject() noexcept

Return the offset object.

inline type operator()() noexcept

Compute the Amplifier output, given the input as stored in the store.

inline type operator()(type input) noexcept

Compute the Amplifier output, given an input.

inline type output() const noexcept

Return the output value, or 0 when not available.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::FirstOrderFilter

template<typename Container, bool LowPass, unsigned long long flags = 0, typename T = float>
class FirstOrderFilter

First-order low- or high-pass filter, based on store variables.

To use this class, add a scope to your store, like:

{
    (float) sample frequency (Hz)
    float input
    float cutoff frequency (Hz)
    bool=true enable
    bool reset
    float=nan override
    float output
} filter

Only sample frequency and cutoff frequency are mandatory. All variables of type float, except for sample frequency, can be any other type, as long as it matches the template parameter T.

Then, instantiate the controller like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto filter_o = stored::LowPass<stored::YourStore>::objects("/filter/");

// Instantiate the filter, tailored to the available fields in the store.
stored::LowPass<stored::YourStore, filter_o.flags()> filter{filter_o, yourStore};

// ...or use HighPass instead of LowPass.

The cutoff frequency can be changed while running (by setting reset to true). It will applied smoothly; the output will gradually take the new cutoff frequency into account.

Public Types

using Bound = typename FirstOrderFilterObjects<Container, type>::template Bound<flags>
using type = T

Public Functions

constexpr FirstOrderFilter() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr FirstOrderFilter(FirstOrderFilterObjects<Container, type> const &o, Container &container)

Initialize the filter, given a list of objects and a container.

inline type cutoffFrequency() const noexcept

Return the cutoff frequency value.

inline decltype(auto) cutoffFrequencyObject() const noexcept

Return the cutoff frequency object.

inline decltype(auto) cutoffFrequencyObject() noexcept

Return the cutoff frequency object.

inline void disable() noexcept

Disable the pulse wave.

Ignored when the enable object is not available.

inline void enable(bool value = true) noexcept

Enable (or disable) the pulse wave.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, or true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type input() const noexcept

Return the input value, or 0 when not available.

inline decltype(auto) inputObject() const noexcept

Return the input object.

inline decltype(auto) inputObject() noexcept

Return the input object.

inline type lastInput() const noexcept

Return the last input to the filter.

inline type lastOutput() const noexcept

Return the last output of the filter.

inline type operator()() noexcept

Compute filter output, given the input stored in the store.

inline type operator()(type input) noexcept

Compute filter output, given an input.

inline type output() const noexcept

Return the output value, or 0 when not available.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline void recomputeCoefficients()

Recompute alpha after changed filter parameters.

inline bool reset() const noexcept

Return the reset value, or false when not available.

inline decltype(auto) resetObject() const noexcept

Return the reset object.

inline decltype(auto) resetObject() noexcept

Return the reset object.

inline float sampleFrequency() const noexcept

Return the sample frequency.

inline decltype(auto) sampleFrequencyObject() const noexcept

Return the sample frequency object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

There are stored::LowPass and stored::HighPass aliases for the corresponding stored::FistOrderFilter template parameters.

stored::PID

template<typename Container, unsigned long long flags = 0, typename T = float>
class PID

PID controller, based on store variables.

To use this class, add a scope to your store, like:

{
    (float) frequency (Hz)
    float y
    float setpoint
    bool=true enable
    float=1 Kp
    float=inf Ti (s)
    float=0 Td (s)
    float=0 Kff
    float int
    float=-inf int low
    float=inf int high
    float=-inf low
    float=inf high
    float=inf error max
    float=inf epsilon
    bool reset
    float=nan override
    float u
} pid

Only frequency, setpoint, and Kp are mandatory. All variables of type float, except for frequency, can be any other type, as long as it matches the template parameter T.

It has the following objects:

  • frequency: the control frequency; the application must invoke the PID controller at this frequency

  • y: the process variable (output of the plant)

  • setpoint: the setpoint to control y to

  • Kp: P coefficient

  • Ti: I time constant

  • Td: D time constant

  • Kff: feed-forward coefficient

  • int: current integral value

  • int low: lower bound for int

  • int high: upper bound for int

  • low: lower bound for computed u

  • high: upper bound for computed u

  • epsilon: minimum value of the error ( | setpoint - y | ), which must result in a change of the output u. Otherwise, numerical stability is compromised. See isHealthy().

  • reset: when set to true, recompute and apply changed control parameters

  • override: when not NaN, force u to this value, without low and high applied

  • u: control output (input for the plant)

Then, instantiate the controller like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto pid_o = stored::PID<stored::YourStore>::objects("/pid/");

// Instantiate a PID, tailored to the available fields in the store.
stored::PID<stored::YourStore, pid_o.flags()> pid{pid_o, yourStore};

The PID controller has the following properties:

  • The parameters specify a serial PID.

  • The integral windup prevention stops the integral when the output clips.

  • Changing Ti is implemented smoothly; changing the parameters (and setting reset afterwards) can be done while running.

  • isHealthy() checks for numerical stability.

Public Types

using Bound = typename PIDObjects<Container, type>::template Bound<flags>
using type = T

Public Functions

constexpr PID() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr PID(PIDObjects<Container, type> const &o, Container &container)

Initialize the pin, given a list of objects and a container.

inline void disable() noexcept

Disable the PID.

Ignored when the enable object is not available.

inline void enable(bool value = true) noexcept

Enable (or disable) the PID.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, or true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type epsilon() const noexcept

Return the epsilon value, or inf when not available.

inline decltype(auto) epsilonObject() const noexcept

Return the epsilon object.

inline decltype(auto) epsilonObject() noexcept

Return the epsilon object.

inline type errorMax() const noexcept

Return the error max value, or inf when not available.

inline decltype(auto) errorMaxObject() const noexcept

Return the error max object.

inline decltype(auto) errorMaxObject() noexcept

Return the error max object.

inline float frequency() const noexcept

Return the control frequency.

inline decltype(auto) frequencyObject() const noexcept

Return the frequency object.

inline type high() const noexcept

Return the high value, or inf when not available.

inline decltype(auto) highObject() const noexcept

Return the high object.

inline decltype(auto) highObject() noexcept

Return the high object.

inline type int_() const noexcept

Return the current integral value.

This is the integral of the (setpoint - y) * Ki. So, when Ti (and there fore Ki) changes, it may take a while till the new Ti is in effect, depending on the current integral value.

inline type intHigh() const noexcept

Return the int high vale, or inf when not available.

inline decltype(auto) intHighObject() const noexcept

Return the int high object.

inline decltype(auto) intHighObject() noexcept

Return the int high object.

inline type intLow() const noexcept

Return the int low value, or -inf when not available.

inline decltype(auto) intLowObject() const noexcept

Return the int low object.

inline decltype(auto) intLowObject() noexcept

Return the int low object.

inline decltype(auto) intObject() const noexcept

Return the int object.

inline decltype(auto) intObject() noexcept

Return the int object.

inline bool isHealthy() const noexcept

Check numerical stability.

epsilon() is the smallest change in error ( | setpoint() - y() | ) that must have influence the output u(). If the error is smaller, the output may remain the same. This function checks if that is still the case.

The integrator is especially interesting. If it becomes too large, successive (small) errors may not be able to reduce it anymore, because of rounding. If that is the case, it is considered unhealthy.

This value can optionally be defined in the store. If omitted, this function always returns true.

You may want to check (or assert on) this function once in a while, like once per second or after every run, to detect a stuck controller within reasonable time for your application.

inline type Kd() const noexcept

Return the computed Kd value.

inline type Kff() const noexcept

Return the Kff value, or 0 when not available.

inline decltype(auto) KffObject() const noexcept

Return the Kff object.

inline decltype(auto) KffObject() noexcept

Return the Kff object.

inline type Ki() const noexcept

Return the computed Ki value.

inline type Kp() const noexcept

Return the Kp value.

inline decltype(auto) KpObject() const noexcept

Return the Kp object.

inline decltype(auto) KpObject() noexcept

Return the Kp object.

inline type low() const noexcept

Return the low value, or -inf when not available.

inline decltype(auto) lowObject() const noexcept

Return the low object.

inline decltype(auto) lowObject() noexcept

Return the low object.

inline type operator()() noexcept

Compute the PID output, given the y as stored in the store.

inline type operator()(type y) noexcept

Compute the PID output, given a y.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline bool reset() const noexcept

Return the reset value, or false when not available.

inline decltype(auto) resetObject() const noexcept

Return the reset object.

inline decltype(auto) resetObject() noexcept

Return the reset object.

inline type setpoint() const noexcept

Return the setpoint value.

inline decltype(auto) setpointObject() const noexcept

Return the setpoint object.

inline decltype(auto) setpointObject() noexcept

Return the setpoint object.

inline type Td() const noexcept

Return the Td value, or 0 when not available.

inline decltype(auto) TdObject() const noexcept

Return the Td object.

inline decltype(auto) TdObject() noexcept

Return the Td object.

inline type Ti() const noexcept

Return the Ti value, or inf when not available.

inline decltype(auto) TiObject() const noexcept

Return the Ti object.

inline decltype(auto) TiObject() noexcept

Return the Ti object.

inline type u() const noexcept

Return the u value, with the override applied.

inline decltype(auto) uObject() const noexcept

Return the u object.

inline decltype(auto) uObject() noexcept

Return the u object.

inline type y() const noexcept

Return the y value, or 0 when not available.

inline decltype(auto) yObject() const noexcept

Return the y object.

inline decltype(auto) yObject() noexcept

Return the y object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::PinIn

template<typename Container, unsigned long long flags = 0>
class PinIn

An GPIO input pin, based on store variables.

This class comes in very handy when a GPIO input should be observed and overridden while debugging. It gives some interface between the hardware pin and the input that the application sees.

To use this class, add a scope to your store, like:

{
    (bool) pin
    int8=-1 override
    bool input
    (bool) get
} pin

All fields are optional. You can implement the store’s pin function, override the virtual pin() function of the PinIn class, or pass the hardware pin value as an argument to the PinIn::operator().

The pin basically does:

switch(override) {
case -1: input = pin; break;
case  0: input = false; break;
case  1: input = true; break;
case  2: input = !pin; break;
}

Then, instantiate the pin like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto pin_o = stored::PinIn<stored::YourStore>::objects("/pin/");

// Instantiate an PinIn, tailored to the available fields in the store.
stored::PinIn<stored::YourStore, pin_o.flags()> pin{pin_o, yourStore};

When pin() is called, it will invoke the pin function to get the actual hardware pin status. Then, it will set the input variable.

The get function is not used/provided by this PinIn. Implement this store function such that it calls and returns pin(). When applications read the get function, they will always get the appropriate/actual pin value.

Public Types

using Bound = typename PinInObjects<Container>::template Bound<flags>

Public Functions

constexpr PinIn() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr PinIn(PinInObjects<Container> const &o, Container &container)

Initialize the pin, given a list of objects and a container.

virtual ~PinIn() = default

Dtor.

inline bool input() const noexcept

Return the last computed input value, or compute the pin state when the object is not available.

inline decltype(auto) inputObject() const noexcept

Return the input object.

inline decltype(auto) inputObject() noexcept

Return the input object.

inline bool operator()() noexcept

Determine pin input, given the current hardware state.

inline bool operator()(bool pin) noexcept

Determine pin input, given the provided hardware state.

inline int8_t override_() const noexcept

Return the override value, or -1 when the object is not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline virtual bool pin() const noexcept

Return the hardware pin value.

By default, it calls the pin function in the store. Override in a subclass to implement other behavior.

inline decltype(auto) pinObject() const noexcept

Return the pin object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::PinOut

template<typename Container, unsigned long long flags = 0>
class PinOut

An GPIO output pin, based on store variables.

This class comes in very handy when a GPIO output should be observed and overridden while debugging. It gives some interface between the hardware pin and the output that the application wants.

To use this class, add a scope to your store, like:

{
    (bool) set
    bool output
    (int8) override
    (bool) pin
} pin

All fields are optional, except output. You can implement the store’s pin function, override the virtual pin() function of the PinOut class, or forward the return value of PinOut::operator() to the hardware pin.

The pin basically does:

switch(override) {
case -1: pin = output; break;
case  0: pin = false; break;
case  1: pin = true; break;
case  2: pin = !output; break;
}

Then, instantiate the pin like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto pin_o = stored::PinOut<stored::YourStore>::objects("/pin/");

// Instantiate an PinOut, tailored to the available fields in the store.
stored::PinOut<stored::YourStore, pin_o.flags()> pin{pin_o, yourStore};

The set function is not used/provided by this PinOut. Implement this store function such that it calls pin() with the provided value. When applications write the set function, they will immediately control the hardware pin.

Similar holds for the override function; implement it to call the override_() of PinOut. This way, if one sets the override value, the hardware pin is updated accordingly.

Public Types

using Bound = typename PinOutObjects<Container>::template Bound<flags>

Public Functions

constexpr PinOut() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr PinOut(PinOutObjects<Container> const &o, Container &container)

Initialize the pin, given a list of objects and a container.

virtual ~PinOut() = default

Dtor.

inline bool operator()() noexcept

Compute and set the hardware pin status, given the last provided application’s output value.

inline bool operator()(bool output) noexcept

Compute and set the hardware pin status, given the application’s output value.

inline bool output() const noexcept

Return the output value.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline int8_t override_() const noexcept

Return the override value.

inline void override_(int8_t x) noexcept

Set the override value.

inline virtual void pin(bool value) noexcept

Set the hardware pin state.

The default implementation calls the store’s pin function. Override in a subclass to implement custom behavior.

inline decltype(auto) pinObject() noexcept

Return the pin object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::PulseWave

template<typename Container, unsigned long long flags = 0, typename T = float>
class PulseWave

Pulse wave generator, based on store variables.

To use this class, add a scope to your store, like:

{
    (float) sample frequency (Hz)
    float=1 amplitude
    float=1 frequency (Hz)
    float=0 phase (rad)
    float=0.5 duty cycle
    bool=true enable
    float=nan override
    float output
} pulse

Only sample frequency is mandatory. All variables of type float, except for sample frequency, can be any other type, as long as it matches the template parameter T.

When either override or output is omitted, names may become ambiguous. In that case, provide the ids of the fields that are in the store, as template parameters to objects():

field

id

sample frequency

s

amplitude

A

frequency

f

phase

p

duty cycle

d

enable

e

override

F

output

O

Then, instantiate the controller like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto pulse_o = stored::PulseWave<stored::YourStore>::objects("/pulse/");

// Instantiate the generator, tailored to the available fields in the store.
stored::PulseWave<stored::YourStore, pulse_o.flags()> pulse{pulse_o, yourStore};

Public Types

using Bound = typename PulseWaveObjects<Container, type>::template Bound<flags>
using type = T

Public Functions

constexpr PulseWave() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr PulseWave(PulseWaveObjects<Container, type> const &o, Container &container)

Initialize the pulse wave, given a list of objects and a container.

inline type amplitude() const noexcept

Return the amplitude value, or 1 when not available.

inline decltype(auto) amplitudeObject() const noexcept

Return the amplitude object.

inline decltype(auto) amplitudeObject() noexcept

Return the amplitude object.

inline void disable() noexcept

Disable the pulse wave.

Ignored when the enable object is not available.

inline type dutyCycle() const noexcept

Return the duty cycle value, or 0.5 when not available.

inline decltype(auto) dutyCycleObject() const noexcept

Return the duty cycle object.

inline decltype(auto) dutyCycleObject() noexcept

Return the duty cycle object.

inline void enable(bool value = true) noexcept

Enable (or disable) the pulse wave.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, or true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type frequency() const noexcept

Return the frequency value, or 1 when not specified.

inline decltype(auto) frequencyObject() const noexcept

Return the frequency object.

inline decltype(auto) frequencyObject() noexcept

Return the frequency object.

inline bool isHealthy() const noexcept

Check numerical stability.

This function checks if for every control interval (1 / sampleFrequency()), the output is actually updated. Especially the period and phase values are checked if they are not too big.

You may want to check (or assert on) this function once in a while, like once per second or after every run, to detect a stuck controller within reasonable time for your application.

inline type operator()() noexcept

Compute the pulse wave output.

inline type output() const noexcept

Return the output value, or 0 when not available.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline type phase() const noexcept

Return the phase value, or 0 when not available.

inline decltype(auto) phaseObject() const noexcept

Return the phase object.

inline decltype(auto) phaseObject() noexcept

Return the phase object.

inline float sampleFrequency() const noexcept

Return the sample frequency.

inline decltype(auto) sampleFrequencyObject() const noexcept

Return the sample frequency object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::Ramp

template<typename Container, unsigned long long flags = 0, typename T = float>
class Ramp

Ramping setpoints, based on store variables.

This is a quadratic path planner, that creates a smooth path from the current output towards the provided input. The speed and acceleration can be limited.

To use this class, add a scope to your store, like:

{
    (float) sample frequency (Hz)
    float input
    float=inf speed limit
    float=inf acceleration limit
    bool reset
    bool=true enable
    float=nan override
    float output
} ramp

Only sample frequency is mandatory. All variables of type float, except for sample frequency, can be any other type, as long as it matches the template parameter T.

Then, instantiate the controller like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto ramp_o = stored::Ramp<stored::YourStore>::objects("/ramp/");

// Instantiate the generator, tailored to the available fields in the store.
stored::Ramp<stored::YourStore, ramp_o.flags()> ramp{ramp_o, yourStore};

The parameters can be changed while running (when reset is set to true). The change will be applied smoothly to the path.

Public Types

using Bound = typename RampObjects<Container, type>::template Bound<flags>
using type = T
using type_ = long

Public Functions

constexpr Ramp() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr Ramp(RampObjects<Container, type> const &o, Container &container)

Initialize the ramp, given a list of objects and a container.

inline type accelerationLimit() const noexcept

Return the acceleration limit value, or inf when not available.

inline decltype(auto) accelerationLimitObject() const noexcept

Return the acceleration limit object.

inline decltype(auto) accelerationLimitObject() noexcept

Return the acceleration limit object.

inline void disable() noexcept

Disable the pulse wave.

Ignored when the enable object is not available.

inline void enable(bool value = true) noexcept

Enable (or disable) the ramp.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, or true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type input() const noexcept

Return the input value, or 0 when not available.

inline decltype(auto) inputObject() const noexcept

Return the input object.

inline decltype(auto) inputObject() noexcept

Return the input object.

inline bool isHealthy() const noexcept

Check numerical stability.

The Ramp is considered healthy when the configured acceleration and speed values are within the floating point precision.

You may want to check (or assert on) this function once in a while, like once per second or after every run, to detect a stuck ramp within reasonable time for your application.

inline type operator()() noexcept

Compute the next ramp output, given the input in the store.

inline type operator()(type input) noexcept

Compute the next ramp output, given an input.

inline type output() const noexcept

Return the output value, or 0 when not available.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline bool reset() const noexcept

Return the reset value, or false when not available.

inline decltype(auto) resetObject() const noexcept

Return the reset object.

inline decltype(auto) resetObject() noexcept

Return the reset object.

inline float sampleFrequency() const noexcept

Return the sample frequency.

inline decltype(auto) sampleFrequencyObject() const noexcept

Return the sample frequency object.

inline type speedLimit() const noexcept

Return the speed limit value, or inf when not available.

inline decltype(auto) speedLimitObject() const noexcept

Return the speed limit object.

inline decltype(auto) speedLimitObject() noexcept

Return the speed limit object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.

stored::Sine

template<typename Container, unsigned long long flags = 0, typename T = float>
class Sine

Sine wave generator, based on store variables.

To use this class, add a scope to your store, like:

{
    (float) sample frequency (Hz)
    float=1 amplitude
    float=0.159 frequency (Hz)
    float=0 phase (rad)
    float=0 offset
    bool=true enable
    float=nan override
    float output
} sine

Only sample frequency is mandatory. All variables of type float, except for sample frequency, can be any other type, as long as it matches the template parameter T.

When either override or output is omitted, names may become ambiguous. In that case, provide the ids of the fields that are in the store, as template parameters to objects():

field

id

sample frequency

s

amplitude

A

frequency

f

phase

p

offset

o

enable

e

override

F

output

O

Then, instantiate the sine wave generator like this:

// Construct a compile-time object, which resolves all fields in your store.
constexpr auto sine_o = stored::Sine<stored::YourStore>::objects("/sine/");

// Instantiate the generator, tailored to the available fields in the store.
stored::Sine<stored::YourStore, sine_o.flags()> sine{sine_o, yourStore};

When the parameters of the sine wave are changed while running, they are applied immediately, without a smooth transition.

Public Types

using Bound = typename SineObjects<Container, type>::template Bound<flags>
using type = T

Public Functions

constexpr Sine() noexcept = default

Default ctor.

Use this when initialization is postponed. You can assign another instance later on.

inline constexpr Sine(SineObjects<Container, type> const &o, Container &container)

Initialize the sine, given a list of objects and a container.

inline type amplitude() const noexcept

Return the amplitude value, or 1 when not available.

inline decltype(auto) amplitudeObject() const noexcept

Return the amplitude object.

inline decltype(auto) amplitudeObject() noexcept

Return the amplitude object.

inline void disable() noexcept

Disable the sine wave.

Ignored when the enable object is not available.

inline void enable(bool value = true) noexcept

Enable (or disable) the sine wave.

Ignored when the enable object is not available.

inline bool enabled() const noexcept

Return the enable value, or true when not available.

inline decltype(auto) enableObject() const noexcept

Return the enable object.

inline decltype(auto) enableObject() noexcept

Return the enable object.

inline type frequency() const noexcept

Return the frequency value, or 1/2pi when not specified.

inline decltype(auto) frequencyObject() const noexcept

Return the frequency object.

inline decltype(auto) frequencyObject() noexcept

Return the frequency object.

inline bool isHealthy() const noexcept

Check numerical stability.

This function checks if for every control interval (1 / sampleFrequency()), the output is actually updated. Especially the period and phase values are checked if they are not too big.

You may want to check (or assert on) this function once in a while, like once per second or after every run, to detect a stuck controller within reasonable time for your application.

inline type offset() const noexcept

Return the offset value, or 0 when not available.

inline decltype(auto) offsetObject() const noexcept

Return the offset object.

inline decltype(auto) offsetObject() noexcept

Return the offset object.

inline type operator()() noexcept

Compute the sine output.

inline type output() const noexcept

Return the output value, or 0 when not available.

inline decltype(auto) outputObject() const noexcept

Return the output object.

inline decltype(auto) outputObject() noexcept

Return the output object.

inline type override_() const noexcept

Return the override value, or NaN when not available.

inline decltype(auto) overrideObject() const noexcept

Return the override object.

inline decltype(auto) overrideObject() noexcept

Return the override object.

inline type phase() const noexcept

Return the phase value, or 0 when not available.

inline decltype(auto) phaseObject() const noexcept

Return the phase object.

inline decltype(auto) phaseObject() noexcept

Return the phase object.

inline float sampleFrequency() const noexcept

Return the sample frequency.

inline decltype(auto) sampleFrequencyObject() const noexcept

Return the sample frequency object.

Public Static Functions

template<char... OnlyId, size_t N>
static inline constexpr auto objects(char const (&prefix)[N]) noexcept

Create the list of objects in the store, used to compute the flags parameter.