Digital Signal Processing (DSP) library¶
The DSP library included with Falcon contains a number of modular and reusable signal processing algorithms that can be used inside processor nodes.
Detect threshold crossing¶
Given the value of a single data sample, an instance of the ThresholdCrosser class detects if a threshold has been crossed. It does so by keeping track of the previous sample value and testing if the current and previous data samples are on opposite sides of the threshold. For example, for an upward threshold crossing, a crossing is detected if the previous sample is smaller than or equal to the threshold and the current sample is larger than the threshold.
Operation of the ThresholdCrosser is determined by two parameters: the value of the threshold and the direction of the crossing. These parameters are set in the constructor or using member functions (set_threshold and set_slope). Threshold crossing are detected by repeatedly calling the has_crossed member function with the next data sample.
Here is an example of how to use the ThresholdCrosser class:
// create instance with threshold = 10. and slope = UP
auto t = dsp::algorithms::ThresholdCrosser( 10., Slope::UP );
for (double k=0.5; k<20; ++k) {
if (t.has_crossed(k)) {
std::cout << "Threshold crossed!";
}
}
-
class ThresholdCrosser
Compute running statistics of signal¶
The RunningStatistics class supports the computation of running center and dispersion measures of a signal. It is a virtual base class with currently a single concrete subclass RunningMeanMAD that overrides the virtual method update_statistics to compute the running mean and mean absolute deviation.
The weight of the most recent data sample is set by the alpha parameter, which take a value between 0 and 1. The update_statistics method takes a data sample and alpha value and updates the internal estimates of the center and dispersion measures. For example, the RunningMeanMad class computes the running mean as \((1-alpha) \times mean + alpha \times sample\).
Optionally, a burn-in period can be set during which the alpha value gradually changes from 1. to the preset alpha value. This has the effect that only weight is given to the data samples and not the initial values of the center and dispersion measures.
Also optionally, data samples that are more than a certain distance (in multiples of the z-score) from the center (i.e. outliers) have reduced influence on the computation of the running statistics.
Here is an example of how to use the RunningMeanMAD class:
std::vector<double> samples{0.1, 2.0, 1.5, 3.2, 1.3, 2.4};
// set alpha parameter to 0.1, no burn-in or outlier detection
r = dsp::algorithms::RunningMeanMAD(0.1);
r.add_samples(samples);
std::cout << "running mean = " << r.mean() << " and running MAD = " << r.mad();
-
class RunningStatistics
Subclassed by dsp::algorithms::RunningMeanMAD
Public Functions
-
RunningStatistics(double alpha, uint64_t burn_in = 0, bool outlier_protection = false, double outlier_zscore = 3, double outlier_half_life = 1, double center = 0.0, double dispersion = 0.0)
-
double alpha() const
-
uint64_t burn_in() const
-
double center() const
-
double dispersion() const
-
bool outlier_protection() const
-
double outlier_zscore() const
-
double outlier_half_life() const
-
bool is_burning_in() const
-
double zscore(double value) const
-
void set_center(double value)
-
void set_dispersion(double value)
-
void set_alpha(double value)
-
void set_burn_in(uint64_t value)
-
void set_outlier_protection(bool value)
-
void set_outlier_zscore(double value)
-
void set_outlier_half_life(double value)
-
void add_sample(double sample)
-
void add_samples(std::vector<double> samples)
-
RunningStatistics(double alpha, uint64_t burn_in = 0, bool outlier_protection = false, double outlier_zscore = 3, double outlier_half_life = 1, double center = 0.0, double dispersion = 0.0)
Detect local peaks¶
The PeakDetector class detects local peaks in a signal, gives access to the timestamp and amplitude of the last detected peak and keeps track of the total number of detected peaks.
-
class PeakDetector
Public Functions
-
inline PeakDetector(uint64_t init_timestamp = 0, double init_value = 0.0)
-
void reset(uint64_t init_timestamp = 0, double init_value = 0.0)
-
double last_peak_amplitude() const
-
uint64_t last_peak_timestamp() const
-
bool is_peak(const uint64_t ×tamp, const double &sample)
-
bool upslope() const
-
uint64_t npeaks() const
-
inline PeakDetector(uint64_t init_timestamp = 0, double init_value = 0.0)
Exponentially smooth signal¶
The ExponentialSmoother class smooths a signal sample by sample. The integration window for smoothing is determined by the alpha parameter that sets the weight of the new data sample, i.e. \(value = value * (1-alpha) + sample * alpha\). Here is an example:
double smooth_sample;
// create smoother with alpha = 0.1
auto s = dsp::algorithms::ExponentialSmoother(0.1);
std::vector<double> samples{0.1, 2.0, 1.5, 3.2, 1.3, 2.4};
for (auto k : samples) {
smooth_sample = s.smooth(k);
}
-
class ExponentialSmoother
Public Functions
-
ExponentialSmoother(double alpha, double init_value = 0.0)
-
double smooth(double value)
-
double alpha() const
-
void set_alpha(double value)
-
double value() const
-
void set_value(double value)
-
ExponentialSmoother(double alpha, double init_value = 0.0)
Detect spikes¶
This algorithm operates sample by sample and looks for upwards deflections in at least one of the channels above a certain threshold.
A spike is detected if the signal of at least one channel crosses the threshold and a local maxima is found in at least one channel (not necessarily the same of that of threshold crossing) within a certain duration (determined by the peak lifetime) The timestamp of the detected spike corresponds to that one of the first sample that crossed the threshold first (independently on whether that sample belongs to the current or previous buffers) In case a proper maximum is found on all channels, the peak values are returned, together with the threshold-crossing timestamp; however, if on one or more channels no peaks were found, the values of the signals at the threshold-crossing sample will be returned.
-
class SpikeDetector
Public Functions
-
SpikeDetector(unsigned int nchannels, double threshold, unsigned int peak_life_time)
-
inline ~SpikeDetector()
-
void reset()
-
unsigned int nchannels() const
-
double threshold() const
-
void set_threshold(double value)
-
unsigned int peak_life_time() const
-
void set_peak_life_time(unsigned int value)
-
uint64_t timestamp_detected_spike() const
-
const std::vector<double> &litudes_detected_spike() const
-
template<typename ForwardIterator>
inline bool is_spike(const uint64_t timestamp, const ForwardIterator sample) Spike detection algorithm:
operates sample by sample
looks for upwards deflections in at least one of the channels above a certain threshold
a spike is detected if the signal of at least one channel crosses the threshold and a local maxima is found in at least one channel (not necessarily the same of that of threshold crossing) within a certain duration (determined by the peak lifetime)
the timestamp of the detected spike corresponds to that one of the first sample that crossed the threshold first (independently on whether that sample belongs to the current or previous buffers)
in case a proper maximum is found on all channels, the peak values are returned, together with the threshold-crossing timestamp; however, if on one or more channels no peaks were found, the values of the signals at the threshold-crossing sample will be returned.
-
bool is_spike(const uint64_t timestamp, const std::vector<double> &sample)
-
uint64_t nspikes() const
-
SpikeDetector(unsigned int nchannels, double threshold, unsigned int peak_life_time)
Filtering¶
Finite impulse response (FIR) filters
Infinite impulse response (IIR) filters
-
namespace filter¶
Functions
-
std::map<std::string, std::string> parse_file_header(std::istream &stream)
-
IFilter *construct_from_file(std::string file)
-
IFilter *construct_from_yaml(const YAML::Node &node)
-
class IFilter
Subclassed by dsp::filter::BiquadFilter, dsp::filter::FirFilter
Public Functions
-
inline IFilter(std::string description)
-
virtual ~IFilter()
-
std::string description() const
-
virtual IFilter *clone() = 0
-
virtual unsigned int order() const = 0
-
unsigned int nchannels() const
-
bool realized() const
-
void realize(unsigned int nchannels, double init = 0.0)
-
void unrealize()
-
virtual double process_channel(double, unsigned int channel) = 0
-
virtual void process_sample(std::vector<double>&, std::vector<double>&) = 0
-
virtual void process_sample(std::vector<double>::iterator, std::vector<double>::iterator) = 0
-
virtual void process_sample(double*, double*) = 0
-
virtual void process_channel(std::vector<double>&, std::vector<double>&, unsigned int channel = 0) = 0
-
virtual void process_channel(uint64_t nsamples, std::vector<double>::iterator, std::vector<double>::iterator, unsigned int channel = 0) = 0
-
virtual void process_channel(uint64_t nsamples, double*, double*, unsigned int channel) = 0
-
virtual void process_by_channel(std::vector<std::vector<double>>&, std::vector<std::vector<double>>&) = 0
-
virtual void process_by_sample(std::vector<std::vector<double>>&, std::vector<std::vector<double>>&) = 0
-
virtual void process_by_channel(uint64_t nsamples, double**, double**) = 0
-
virtual void process_by_sample(uint64_t nsamples, double**, double**) = 0
-
virtual void process_by_channel(uint64_t nsamples, std::vector<double>&, std::vector<double>&) = 0
-
virtual void process_by_sample(uint64_t nsamples, std::vector<double>&, std::vector<double>&) = 0
-
inline IFilter(std::string description)
-
class FirFilter : public dsp::filter::IFilter
Subclassed by dsp::filter::SlopeFilter
Public Functions
-
FirFilter(const std::vector<double> &coefficients, std::string description = "")
-
virtual IFilter *clone()
-
virtual unsigned int order() const final
-
std::size_t group_delay() const
-
virtual double process_channel(double input, unsigned int channel = 0) final
-
virtual void process_sample(std::vector<double> &input, std::vector<double> &output) final
-
virtual void process_sample(std::vector<double>::iterator input, std::vector<double>::iterator output) final
-
virtual void process_sample(double *input, double *output) final
-
virtual void process_channel(std::vector<double> &input, std::vector<double> &output, unsigned int channel = 0) final
-
virtual void process_channel(uint64_t nsamples, std::vector<double>::iterator input, std::vector<double>::iterator output, unsigned int channel = 0)
-
virtual void process_channel(uint64_t nsamples, double *input, double *output, unsigned int channel = 0)
-
virtual void process_by_channel(std::vector<std::vector<double>> &input, std::vector<std::vector<double>> &output) final
-
virtual void process_by_sample(std::vector<std::vector<double>> &input, std::vector<std::vector<double>> &output) final
-
virtual void process_by_channel(uint64_t nsamples, double **input, double **output) final
-
virtual void process_by_sample(uint64_t nsamples, double **input, double **output) final
-
virtual void process_by_channel(uint64_t nsamples, std::vector<double> &input, std::vector<double> &output)
-
virtual void process_by_sample(uint64_t nsamples, std::vector<double> &input, std::vector<double> &output)
Public Static Functions
-
static FirFilter *FromStream(std::istream &stream, std::string description, bool binary = false)
-
FirFilter(const std::vector<double> &coefficients, std::string description = "")
-
class SlopeFilter : public dsp::filter::FirFilter
Public Functions
-
inline SlopeFilter(uint32_t window_size, uint32_t order, uint32_t derivative_order, std::string description = "")
-
inline virtual IFilter *clone()
Public Static Functions
-
static SlopeFilter *FromStream(std::istream &stream, std::string description, bool binary)
Public Static Attributes
-
static constexpr uint16_t DEFAULT_WINDOW_SIZE = 4
-
static constexpr uint8_t DEFAULT_ORDER = 1
-
static constexpr uint8_t DEFAULT_DERIVATIVE_ORDER = 1
-
inline SlopeFilter(uint32_t window_size, uint32_t order, uint32_t derivative_order, std::string description = "")
-
class BiquadFilter : public dsp::filter::IFilter
Public Functions
-
BiquadFilter(double gain, std::vector<std::array<double, 6>> &coefficients, std::string description = "")
-
virtual IFilter *clone()
-
virtual unsigned int order() const final
-
virtual double process_channel(double x, unsigned int c = 0) final
-
inline virtual void process_sample(std::vector<double> &input, std::vector<double> &output) final
-
virtual void process_sample(std::vector<double>::iterator input, std::vector<double>::iterator output) final
-
virtual void process_sample(double *input, double *output) final
-
virtual void process_channel(std::vector<double> &input, std::vector<double> &output, unsigned int channel = 0) final
-
virtual void process_channel(uint64_t nsamples, std::vector<double>::iterator input, std::vector<double>::iterator output, unsigned int channel = 0) final
-
virtual void process_channel(uint64_t nsamples, double *input, double *output, unsigned int channel = 0) final
-
virtual void process_by_channel(std::vector<std::vector<double>> &input, std::vector<std::vector<double>> &output) final
-
virtual void process_by_sample(std::vector<std::vector<double>> &input, std::vector<std::vector<double>> &output) final
-
virtual void process_by_channel(uint64_t nsamples, double **input, double **output) final
-
virtual void process_by_sample(uint64_t nsamples, double **input, double **output) final
-
virtual void process_by_channel(uint64_t nsamples, std::vector<double> &input, std::vector<double> &output)
-
virtual void process_by_sample(uint64_t nsamples, std::vector<double> &input, std::vector<double> &output)
Public Static Functions
-
static BiquadFilter *FromStream(std::istream &stream, std::string description, bool binary)
-
BiquadFilter(double gain, std::vector<std::array<double, 6>> &coefficients, std::string description = "")
-
std::map<std::string, std::string> parse_file_header(std::istream &stream)