Implemented new C++ logic
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
/**
|
||||
* @file SignalBuffer.h
|
||||
* @brief Per-signal ring buffer with LTTB decimation.
|
||||
*
|
||||
* Header-only. Uses STL (this is not a MARTe2 component).
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <cstddef>
|
||||
#include <cmath>
|
||||
#include <algorithm>
|
||||
|
||||
namespace StreamHubClient {
|
||||
|
||||
/**
|
||||
* @brief Thread-safe circular buffer of (time, value) float64 pairs.
|
||||
*/
|
||||
struct SignalBuffer {
|
||||
explicit SignalBuffer(size_t cap = 20000)
|
||||
: capacity(cap), head(0), count(0) {
|
||||
t.resize(cap);
|
||||
v.resize(cap);
|
||||
}
|
||||
|
||||
void setCapacity(size_t cap) {
|
||||
t.assign(cap, 0.0);
|
||||
v.assign(cap, 0.0);
|
||||
capacity = cap;
|
||||
head = 0;
|
||||
count = 0;
|
||||
}
|
||||
|
||||
/** Append a (time, value) pair; overwrites oldest when full. */
|
||||
void push(double time, double val) {
|
||||
t[head] = time;
|
||||
v[head] = val;
|
||||
head = (head + 1) % capacity;
|
||||
if (count < capacity) { count++; }
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy the last `n` points into tOut/vOut (oldest → newest order).
|
||||
* @return Number of points written.
|
||||
*/
|
||||
size_t readLast(size_t n, std::vector<double>& tOut, std::vector<double>& vOut) const {
|
||||
size_t actual = std::min(n, count);
|
||||
if (actual == 0) { return 0; }
|
||||
tOut.resize(actual);
|
||||
vOut.resize(actual);
|
||||
|
||||
size_t startIdx = (head + capacity - actual) % capacity;
|
||||
for (size_t i = 0; i < actual; i++) {
|
||||
size_t idx = (startIdx + i) % capacity;
|
||||
tOut[i] = t[idx];
|
||||
vOut[i] = v[idx];
|
||||
}
|
||||
return actual;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read all points in [t0, t1] into tOut/vOut.
|
||||
* @return Number of points written.
|
||||
*/
|
||||
size_t readRange(double t0, double t1,
|
||||
std::vector<double>& tOut, std::vector<double>& vOut) const {
|
||||
tOut.clear();
|
||||
vOut.clear();
|
||||
if (count == 0) { return 0; }
|
||||
|
||||
size_t startIdx = (head + capacity - count) % capacity;
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
size_t idx = (startIdx + i) % capacity;
|
||||
if (t[idx] >= t0 && t[idx] <= t1) {
|
||||
tOut.push_back(t[idx]);
|
||||
vOut.push_back(v[idx]);
|
||||
}
|
||||
}
|
||||
return tOut.size();
|
||||
}
|
||||
|
||||
size_t size() const { return count; }
|
||||
|
||||
void clear() { head = 0; count = 0; }
|
||||
|
||||
size_t capacity;
|
||||
std::vector<double> t;
|
||||
std::vector<double> v;
|
||||
size_t head;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
||||
/* LTTB — Largest Triangle Three Buckets */
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @brief Decimate tIn/vIn to at most `threshold` points using LTTB.
|
||||
*
|
||||
* Always preserves first and last points.
|
||||
* @return Number of output points.
|
||||
*/
|
||||
inline size_t LTTBDecimate(
|
||||
const std::vector<double>& tIn, const std::vector<double>& vIn,
|
||||
std::vector<double>& tOut, std::vector<double>& vOut,
|
||||
size_t threshold)
|
||||
{
|
||||
const size_t nIn = tIn.size();
|
||||
if (nIn <= threshold || threshold < 2) {
|
||||
tOut = tIn;
|
||||
vOut = vIn;
|
||||
return nIn;
|
||||
}
|
||||
|
||||
tOut.resize(threshold);
|
||||
vOut.resize(threshold);
|
||||
|
||||
tOut[0] = tIn[0];
|
||||
vOut[0] = vIn[0];
|
||||
tOut[threshold - 1] = tIn[nIn - 1];
|
||||
vOut[threshold - 1] = vIn[nIn - 1];
|
||||
|
||||
/* Bucket size (middle threshold-2 buckets cover points 1..nIn-2) */
|
||||
double bucketSize = static_cast<double>(nIn - 2) / static_cast<double>(threshold - 2);
|
||||
|
||||
size_t a = 0; /* index of last selected point */
|
||||
for (size_t i = 0; i < threshold - 2; i++) {
|
||||
/* Calculate average of next bucket */
|
||||
size_t avgRangeStart = static_cast<size_t>((i + 1) * bucketSize) + 1;
|
||||
size_t avgRangeEnd = static_cast<size_t>((i + 2) * bucketSize) + 1;
|
||||
if (avgRangeEnd >= nIn) { avgRangeEnd = nIn - 1; }
|
||||
|
||||
double avgT = 0.0, avgV = 0.0;
|
||||
size_t avgLen = avgRangeEnd - avgRangeStart;
|
||||
for (size_t j = avgRangeStart; j < avgRangeEnd; j++) {
|
||||
avgT += tIn[j];
|
||||
avgV += vIn[j];
|
||||
}
|
||||
if (avgLen > 0) {
|
||||
avgT /= static_cast<double>(avgLen);
|
||||
avgV /= static_cast<double>(avgLen);
|
||||
}
|
||||
|
||||
/* Select point in current bucket with max triangle area */
|
||||
size_t rangeStart = static_cast<size_t>(i * bucketSize) + 1;
|
||||
size_t rangeEnd = static_cast<size_t>((i + 1) * bucketSize) + 1;
|
||||
if (rangeEnd >= nIn) { rangeEnd = nIn - 1; }
|
||||
|
||||
double maxArea = -1.0;
|
||||
size_t maxIdx = rangeStart;
|
||||
for (size_t j = rangeStart; j < rangeEnd; j++) {
|
||||
double area = std::fabs(
|
||||
(tIn[a] - avgT) * (vIn[j] - vIn[a]) -
|
||||
(tIn[a] - tIn[j]) * (avgV - vIn[a])
|
||||
) * 0.5;
|
||||
if (area > maxArea) {
|
||||
maxArea = area;
|
||||
maxIdx = j;
|
||||
}
|
||||
}
|
||||
tOut[i + 1] = tIn[maxIdx];
|
||||
vOut[i + 1] = vIn[maxIdx];
|
||||
a = maxIdx;
|
||||
}
|
||||
return threshold;
|
||||
}
|
||||
|
||||
} /* namespace StreamHubClient */
|
||||
Reference in New Issue
Block a user