Files
2026-06-12 15:25:13 +02:00

118 lines
3.6 KiB
C++

/**
* @file LTTB.h
* @brief Largest Triangle Three Buckets (LTTB) time-series decimation.
*
* Direct C++ translation of the Go lttbDecimate function in wshub/hub.go.
* Preserves visual fidelity when reducing a large time-series to a smaller
* number of representative points.
*
* No external dependencies (uses only <math.h> for fabs).
*/
#ifndef STREAMHUB_LTTB_H_
#define STREAMHUB_LTTB_H_
#include "CompilerTypes.h"
#include <math.h>
namespace StreamHub {
using MARTe::uint32;
using MARTe::float64;
/**
* @brief Decimate time-series (tIn, vIn) of length nIn to at most threshold points.
*
* @param tIn Input time array (float64, monotonically increasing).
* @param vIn Input value array (float64), same length as tIn.
* @param nIn Number of input points.
* @param tOut Output time array (pre-allocated, capacity >= threshold).
* @param vOut Output value array (pre-allocated, capacity >= threshold).
* @param threshold Target number of output points (must be >= 3 for LTTB to apply;
* otherwise all points are copied).
* @return Number of output points written (always <= threshold and <= nIn).
*/
inline uint32 LTTBDecimate(const float64 *tIn, const float64 *vIn, uint32 nIn,
float64 *tOut, float64 *vOut, uint32 threshold)
{
if (nIn == 0u) {
return 0u;
}
if (nIn <= threshold || threshold < 3u) {
/* Fast path: no decimation needed */
for (uint32 i = 0u; i < nIn; i++) {
tOut[i] = tIn[i];
vOut[i] = vIn[i];
}
return nIn;
}
uint32 nOut = 0u;
/* Always include the first point */
tOut[nOut] = tIn[0u];
vOut[nOut] = vIn[0u];
nOut++;
float64 bucketSize = static_cast<float64>(nIn - 2u) /
static_cast<float64>(threshold - 2u);
uint32 a = 0u; /* index of last selected point */
for (uint32 i = 0u; i < threshold - 2u; i++) {
/* Compute average point in the NEXT bucket (for area calculation) */
uint32 avgStart = static_cast<uint32>((static_cast<float64>(i + 1u) * bucketSize)) + 1u;
uint32 avgEnd = static_cast<uint32>((static_cast<float64>(i + 2u) * bucketSize)) + 1u;
if (avgEnd > nIn) { avgEnd = nIn; }
float64 avgT = 0.0;
float64 avgV = 0.0;
uint32 avgCount = avgEnd - avgStart;
if (avgCount > 0u) {
for (uint32 j = avgStart; j < avgEnd; j++) {
avgT += tIn[j];
avgV += vIn[j];
}
avgT /= static_cast<float64>(avgCount);
avgV /= static_cast<float64>(avgCount);
}
/* Find point in current bucket with maximum triangle area */
uint32 bStart = static_cast<uint32>((static_cast<float64>(i) * bucketSize)) + 1u;
uint32 bEnd = static_cast<uint32>((static_cast<float64>(i + 1u) * bucketSize)) + 1u;
if (bEnd > nIn) { bEnd = nIn; }
float64 maxArea = -1.0;
uint32 nextA = bStart;
float64 aT = tIn[a];
float64 aV = vIn[a];
for (uint32 j = bStart; j < bEnd; j++) {
float64 area = fabs((aT - avgT) * (vIn[j] - aV) -
(aT - tIn[j]) * (avgV - aV)) * 0.5;
if (area > maxArea) {
maxArea = area;
nextA = j;
}
}
tOut[nOut] = tIn[nextA];
vOut[nOut] = vIn[nextA];
nOut++;
a = nextA;
}
/* Always include the last point */
tOut[nOut] = tIn[nIn - 1u];
vOut[nOut] = vIn[nIn - 1u];
nOut++;
return nOut;
}
} /* namespace StreamHub */
#endif /* STREAMHUB_LTTB_H_ */