118 lines
3.6 KiB
C++
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_ */
|